@artblocks/abx-sdk 0.1.0-alpha.0

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 (86) hide show
  1. package/LICENSE +21 -0
  2. package/dist/abi/generated.d.ts +6185 -0
  3. package/dist/abi/generated.d.ts.map +1 -0
  4. package/dist/abi/generated.js +28 -0
  5. package/dist/abi/generated.js.map +1 -0
  6. package/dist/abi/index.d.ts +3227 -0
  7. package/dist/abi/index.d.ts.map +1 -0
  8. package/dist/abi/index.js +38 -0
  9. package/dist/abi/index.js.map +1 -0
  10. package/dist/chains.d.ts +27 -0
  11. package/dist/chains.d.ts.map +1 -0
  12. package/dist/chains.js +71 -0
  13. package/dist/chains.js.map +1 -0
  14. package/dist/chunks.d.ts +168 -0
  15. package/dist/chunks.d.ts.map +1 -0
  16. package/dist/chunks.js +232 -0
  17. package/dist/chunks.js.map +1 -0
  18. package/dist/clients.d.ts +42 -0
  19. package/dist/clients.d.ts.map +1 -0
  20. package/dist/clients.js +90 -0
  21. package/dist/clients.js.map +1 -0
  22. package/dist/create2.d.ts +42 -0
  23. package/dist/create2.d.ts.map +1 -0
  24. package/dist/create2.js +47 -0
  25. package/dist/create2.js.map +1 -0
  26. package/dist/deploy.d.ts +192 -0
  27. package/dist/deploy.d.ts.map +1 -0
  28. package/dist/deploy.js +231 -0
  29. package/dist/deploy.js.map +1 -0
  30. package/dist/deployments.d.ts +60 -0
  31. package/dist/deployments.d.ts.map +1 -0
  32. package/dist/deployments.js +81 -0
  33. package/dist/deployments.js.map +1 -0
  34. package/dist/deps.d.ts +109 -0
  35. package/dist/deps.d.ts.map +1 -0
  36. package/dist/deps.js +103 -0
  37. package/dist/deps.js.map +1 -0
  38. package/dist/env.d.ts +2 -0
  39. package/dist/env.d.ts.map +1 -0
  40. package/dist/env.js +45 -0
  41. package/dist/env.js.map +1 -0
  42. package/dist/fastlz.d.ts +20 -0
  43. package/dist/fastlz.d.ts.map +1 -0
  44. package/dist/fastlz.js +123 -0
  45. package/dist/fastlz.js.map +1 -0
  46. package/dist/generator.d.ts +129 -0
  47. package/dist/generator.d.ts.map +1 -0
  48. package/dist/generator.js +81 -0
  49. package/dist/generator.js.map +1 -0
  50. package/dist/index.d.ts +31 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +20 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/ops.d.ts +379 -0
  55. package/dist/ops.d.ts.map +1 -0
  56. package/dist/ops.js +811 -0
  57. package/dist/ops.js.map +1 -0
  58. package/dist/probe.d.ts +30 -0
  59. package/dist/probe.d.ts.map +1 -0
  60. package/dist/probe.js +74 -0
  61. package/dist/probe.js.map +1 -0
  62. package/dist/reconstruct.d.ts +97 -0
  63. package/dist/reconstruct.d.ts.map +1 -0
  64. package/dist/reconstruct.js +597 -0
  65. package/dist/reconstruct.js.map +1 -0
  66. package/dist/spine.d.ts +110 -0
  67. package/dist/spine.d.ts.map +1 -0
  68. package/dist/spine.js +0 -0
  69. package/dist/spine.js.map +1 -0
  70. package/dist/token.d.ts +33 -0
  71. package/dist/token.d.ts.map +1 -0
  72. package/dist/token.js +66 -0
  73. package/dist/token.js.map +1 -0
  74. package/dist/tokendata.d.ts +50 -0
  75. package/dist/tokendata.d.ts.map +1 -0
  76. package/dist/tokendata.js +178 -0
  77. package/dist/tokendata.js.map +1 -0
  78. package/dist/traits.d.ts +38 -0
  79. package/dist/traits.d.ts.map +1 -0
  80. package/dist/traits.js +92 -0
  81. package/dist/traits.js.map +1 -0
  82. package/dist/types.d.ts +145 -0
  83. package/dist/types.d.ts.map +1 -0
  84. package/dist/types.js +2 -0
  85. package/dist/types.js.map +1 -0
  86. package/package.json +53 -0
package/dist/ops.js ADDED
@@ -0,0 +1,811 @@
1
+ import { encodeFunctionData, toHex, zeroAddress } from 'viem';
2
+ import { oneOfOneImageAbi, oneOfOneImageFactoryAbi, oneOfOneImageFactoryBytecode, seriesImageAbi, seriesImageFactoryAbi, seriesImageFactoryBytecode, abxMetadataRendererBytecode, abxFixedPriceMinterAbi, abxFixedPriceMinterBytecode, seriesCodeAbi, seriesCodeFactoryAbi, } from './abi/index.js';
3
+ import { encodeTag } from './spine.js';
4
+ const ZERO_VALUE = '0x0';
5
+ function short(addr) {
6
+ return addr.length > 12 ? `${addr.slice(0, 6)}…${addr.slice(-4)}` : addr;
7
+ }
8
+ /** The `Multicallable.multicall` fragment — identical on every ABX contract (the token
9
+ * and the chunk store both inherit Solady's `Multicallable`), so one fragment batches
10
+ * for any target. */
11
+ const MULTICALL_ABI = [
12
+ {
13
+ type: 'function',
14
+ name: 'multicall',
15
+ stateMutability: 'payable',
16
+ inputs: [{ name: 'data', type: 'bytes[]' }],
17
+ outputs: [{ name: 'results', type: 'bytes[]' }],
18
+ },
19
+ ];
20
+ /**
21
+ * Batch several ops into ONE atomic transaction via the target's
22
+ * `Multicallable.multicall`. This is how a human applies many owner ops — set several
23
+ * fields, point a renderer, lock — in a single signature instead of N transactions.
24
+ *
25
+ * Security: a multicall is *exactly* equivalent to sending each op individually from
26
+ * the same signer — it grants no extra authority (each subcall re-runs its own
27
+ * `onlyOwner`/lock checks under the preserved `msg.sender`) and it's all-or-nothing
28
+ * (any revert bubbles, so nothing partially applies). Enforced here:
29
+ * - every op MUST target the same contract — `multicall` `delegatecall`s into the
30
+ * contract itself, so it can only batch that contract's own functions (it is NOT a
31
+ * cross-contract call primitive; spanning contracts is the *account's* job — an
32
+ * EIP-7702 EOA, ERC-4337 account, or Safe);
33
+ * - every op MUST be value-free — `multicall` reverts on non-zero `msg.value` (the
34
+ * classic multicall double-spend guard), and no ABX op carries value anyway.
35
+ */
36
+ export function prepareMulticall(args) {
37
+ const { ops } = args;
38
+ if (ops.length === 0)
39
+ throw new Error('prepareMulticall: no ops to batch');
40
+ const to = ops[0].to;
41
+ if (to === null)
42
+ throw new Error('prepareMulticall: cannot batch a contract-creation tx');
43
+ for (const o of ops) {
44
+ if (o.to !== to) {
45
+ throw new Error('prepareMulticall: all ops must target the same contract (multicall is delegatecall-to-self, not a cross-contract call)');
46
+ }
47
+ if (o.value !== ZERO_VALUE) {
48
+ throw new Error(`prepareMulticall: op "${o.op}" carries value; multicall must be value-free`);
49
+ }
50
+ }
51
+ return {
52
+ op: 'multicall',
53
+ to,
54
+ data: encodeFunctionData({
55
+ abi: MULTICALL_ABI,
56
+ functionName: 'multicall',
57
+ args: [ops.map((o) => o.data)],
58
+ }),
59
+ value: ZERO_VALUE,
60
+ chainId: args.chainId ?? ops[0].chainId,
61
+ summary: args.summary ?? `Batch ${ops.length} ops in one tx (${ops.map((o) => o.op).join(', ')})`,
62
+ fields: { contract: to, batched: String(ops.length), ops: ops.map((o) => o.op).join(',') },
63
+ };
64
+ }
65
+ /**
66
+ * Collapse a list of ops into the minimal transaction set: same-target runs of ≥2 ops
67
+ * become one `multicall`, lone ops pass through untouched, and contract-creation txs
68
+ * (`to === null`, e.g. a factory/renderer/store deploy) are never folded. Order is
69
+ * preserved — important when a later op depends on an earlier one's effect — so a run is
70
+ * only merged while the target stays the same. This is what turns "6 owner edits" into
71
+ * "1 transaction" for the UX, while staying honest about what genuinely needs its own tx.
72
+ */
73
+ export function batchOps(ops) {
74
+ const out = [];
75
+ let run = [];
76
+ const flush = () => {
77
+ if (run.length === 1)
78
+ out.push(run[0]);
79
+ else if (run.length > 1)
80
+ out.push(prepareMulticall({ ops: run }));
81
+ run = [];
82
+ };
83
+ for (const op of ops) {
84
+ const mergeable = op.to !== null && op.value === ZERO_VALUE;
85
+ if (mergeable && run.length > 0 && run[0].to === op.to) {
86
+ run.push(op);
87
+ }
88
+ else {
89
+ flush();
90
+ run = mergeable ? [op] : [];
91
+ if (!mergeable)
92
+ out.push(op); // standalone (creation or value-bearing)
93
+ }
94
+ }
95
+ flush();
96
+ return out;
97
+ }
98
+ /** Transfer a token to a new holder (a sale/gift settlement). Signer must be `from`. */
99
+ export function prepareTransfer(args) {
100
+ const id = BigInt(args.tokenId);
101
+ return {
102
+ op: 'transfer',
103
+ to: args.contract,
104
+ data: encodeFunctionData({
105
+ abi: oneOfOneImageAbi,
106
+ functionName: 'safeTransferFrom',
107
+ args: [args.from, args.to, id],
108
+ }),
109
+ value: ZERO_VALUE,
110
+ chainId: args.chainId,
111
+ summary: `Transfer token #${id} → ${short(args.to)}`,
112
+ fields: { contract: args.contract, from: args.from, to: args.to, tokenId: id.toString() },
113
+ };
114
+ }
115
+ /**
116
+ * Mint the single token (id 0) to `to`. Owner-only and one-shot — for deferred
117
+ * issuance: deploy first (warm the resolver at the known address), then mint, or
118
+ * mint straight to a buyer on a primary sale. Signer must be the owner.
119
+ */
120
+ export function prepareMint(args) {
121
+ return {
122
+ op: 'mint',
123
+ to: args.contract,
124
+ data: encodeFunctionData({ abi: oneOfOneImageAbi, functionName: 'mint', args: [args.to] }),
125
+ value: ZERO_VALUE,
126
+ chainId: args.chainId,
127
+ // `mint(address)` issues the NEXT sequential token — id #0 for a 1/1, but the next unminted id
128
+ // for a Series (chain-determined at mint time). Don't hardcode "#0" (misleads on a Series where
129
+ // #0 is already minted); say "next" — correct for both.
130
+ summary: `Mint the next token → ${short(args.to)}`,
131
+ fields: { contract: args.contract, to: args.to, token: 'next in order' },
132
+ };
133
+ }
134
+ /** Re-point a token's resolver BASE (move the resolver). The per-token pointer is derived
135
+ * on-chain as `{base}/{chainId}/{address}/{tokenId}`. Affects every token without an
136
+ * override. Signer must be the owner. */
137
+ export function prepareSetTokenURIBase(args) {
138
+ return {
139
+ op: 'set-token-uri-base',
140
+ to: args.contract,
141
+ data: encodeFunctionData({ abi: oneOfOneImageAbi, functionName: 'setTokenURIBase', args: [args.base] }),
142
+ value: ZERO_VALUE,
143
+ chainId: args.chainId,
144
+ summary: `Set tokenURI base → ${args.base}`,
145
+ fields: { contract: args.contract, tokenURIBase: args.base },
146
+ };
147
+ }
148
+ /** Set (or clear, with "") a per-token full-URI override — a fixed locator (e.g. `ipfs://`)
149
+ * that wins over the derived base path for one token. Signer must be the owner. */
150
+ export function prepareSetTokenURIOverride(args) {
151
+ return {
152
+ op: 'set-token-uri-override',
153
+ to: args.contract,
154
+ data: encodeFunctionData({
155
+ abi: oneOfOneImageAbi,
156
+ functionName: 'setTokenURIOverride',
157
+ args: [BigInt(args.tokenId), args.uri],
158
+ }),
159
+ value: ZERO_VALUE,
160
+ chainId: args.chainId,
161
+ summary: args.uri
162
+ ? `Set tokenURI override #${args.tokenId} → ${args.uri}`
163
+ : `Clear tokenURI override #${args.tokenId}`,
164
+ fields: { contract: args.contract, tokenId: String(args.tokenId), tokenURIOverride: args.uri },
165
+ };
166
+ }
167
+ /** Re-point the collection (ERC-7572) resolver BASE; derived on-chain as
168
+ * `{base}/{chainId}/{address}`. Signer must be the owner. */
169
+ export function prepareSetContractURIBase(args) {
170
+ return {
171
+ op: 'set-contract-uri-base',
172
+ to: args.contract,
173
+ data: encodeFunctionData({ abi: oneOfOneImageAbi, functionName: 'setContractURIBase', args: [args.base] }),
174
+ value: ZERO_VALUE,
175
+ chainId: args.chainId,
176
+ summary: `Set contractURI base → ${args.base}`,
177
+ fields: { contract: args.contract, contractURIBase: args.base },
178
+ };
179
+ }
180
+ /** Set (or clear, with "") the collection's full-URI override — a fixed locator that wins
181
+ * over the derived base. Signer must be the owner. */
182
+ export function prepareSetContractURIOverride(args) {
183
+ return {
184
+ op: 'set-contract-uri-override',
185
+ to: args.contract,
186
+ data: encodeFunctionData({ abi: oneOfOneImageAbi, functionName: 'setContractURIOverride', args: [args.uri] }),
187
+ value: ZERO_VALUE,
188
+ chainId: args.chainId,
189
+ summary: args.uri ? `Set contractURI override → ${args.uri}` : 'Clear contractURI override',
190
+ fields: { contract: args.contract, contractURIOverride: args.uri },
191
+ };
192
+ }
193
+ /**
194
+ * Set (or clear) the token's on-chain URI renderer — the toggle between off-chain
195
+ * resolution (`renderer == 0x0`, use the stored `tokenURI`) and on-chain resolution
196
+ * (a non-zero `IAbxMetadataRenderer` that assembles the JSON from on-chain fields).
197
+ * Signer must be the owner; reverts if the URI config is locked.
198
+ */
199
+ export function prepareSetTokenURIRenderer(args) {
200
+ const onChain = args.renderer !== zeroAddress;
201
+ return {
202
+ op: 'set-token-uri-renderer',
203
+ to: args.contract,
204
+ data: encodeFunctionData({
205
+ abi: oneOfOneImageAbi,
206
+ functionName: 'setTokenURIRenderer',
207
+ args: [args.renderer],
208
+ }),
209
+ value: ZERO_VALUE,
210
+ chainId: args.chainId,
211
+ summary: onChain ? `Resolve tokenURI on-chain via ${short(args.renderer)}` : 'Resolve tokenURI off-chain (clear renderer)',
212
+ fields: { contract: args.contract, renderer: args.renderer },
213
+ };
214
+ }
215
+ /** Set (or clear) the collection's on-chain URI renderer. Signer must be the owner. */
216
+ export function prepareSetContractURIRenderer(args) {
217
+ const onChain = args.renderer !== zeroAddress;
218
+ return {
219
+ op: 'set-contract-uri-renderer',
220
+ to: args.contract,
221
+ data: encodeFunctionData({
222
+ abi: oneOfOneImageAbi,
223
+ functionName: 'setContractURIRenderer',
224
+ args: [args.renderer],
225
+ }),
226
+ value: ZERO_VALUE,
227
+ chainId: args.chainId,
228
+ summary: onChain ? `Resolve contractURI on-chain via ${short(args.renderer)}` : 'Resolve contractURI off-chain (clear renderer)',
229
+ fields: { contract: args.contract, renderer: args.renderer },
230
+ };
231
+ }
232
+ /** Freeze the token-URI config (pointer + renderer) forever. Irreversible. Signer = owner. */
233
+ export function prepareLockTokenURI(args) {
234
+ return {
235
+ op: 'lock-token-uri',
236
+ to: args.contract,
237
+ data: encodeFunctionData({ abi: oneOfOneImageAbi, functionName: 'lockTokenURI', args: [] }),
238
+ value: ZERO_VALUE,
239
+ chainId: args.chainId,
240
+ summary: 'Freeze the tokenURI config (pointer + renderer) — permanent',
241
+ fields: { contract: args.contract },
242
+ };
243
+ }
244
+ /** Freeze the collection-URI config forever. Irreversible. Signer = owner. */
245
+ export function prepareLockContractURI(args) {
246
+ return {
247
+ op: 'lock-contract-uri',
248
+ to: args.contract,
249
+ data: encodeFunctionData({ abi: oneOfOneImageAbi, functionName: 'lockContractURI', args: [] }),
250
+ value: ZERO_VALUE,
251
+ chainId: args.chainId,
252
+ summary: 'Freeze the contractURI config (pointer + renderer) — permanent',
253
+ fields: { contract: args.contract },
254
+ };
255
+ }
256
+ /** Set the contract-wide default royalty. Signer must be the owner. */
257
+ export function prepareSetRoyalty(args) {
258
+ if (args.bps < 0 || args.bps > 10000)
259
+ throw new Error(`royalty bps out of range (0–10000): ${args.bps}`);
260
+ return {
261
+ op: 'set-royalty',
262
+ to: args.contract,
263
+ data: encodeFunctionData({
264
+ abi: oneOfOneImageAbi,
265
+ functionName: 'setDefaultRoyalty',
266
+ args: [args.receiver, args.bps],
267
+ }),
268
+ value: ZERO_VALUE,
269
+ chainId: args.chainId,
270
+ summary: `Set royalty → ${(args.bps / 100).toFixed(2)}% to ${short(args.receiver)}`,
271
+ fields: { contract: args.contract, receiver: args.receiver, bps: String(args.bps) },
272
+ };
273
+ }
274
+ /**
275
+ * Set/replace an on-chain metadata field for a token. `field` is what (e.g. "image",
276
+ * "description"), `representation` is how it's carried (e.g. "inline", "keccak256",
277
+ * "arweave"), `value` is the bytes. Reverts once the field is locked. Signer = owner.
278
+ */
279
+ export function prepareSetTokenField(args) {
280
+ const id = BigInt(args.tokenId);
281
+ return {
282
+ op: 'set-field',
283
+ to: args.contract,
284
+ data: encodeFunctionData({
285
+ abi: oneOfOneImageAbi,
286
+ functionName: 'setTokenField',
287
+ args: [id, encodeTag(args.field), encodeTag(args.representation), args.value],
288
+ }),
289
+ value: ZERO_VALUE,
290
+ chainId: args.chainId,
291
+ summary: `Set on-chain ${args.field} (${args.representation}) for token #${id}`,
292
+ fields: { contract: args.contract, tokenId: id.toString(), field: args.field, representation: args.representation, value: args.value },
293
+ };
294
+ }
295
+ /** Freeze a token field forever, across all its representations (irreversible). Signer = owner. */
296
+ export function prepareLockTokenField(args) {
297
+ const id = BigInt(args.tokenId);
298
+ return {
299
+ op: 'lock-field',
300
+ to: args.contract,
301
+ data: encodeFunctionData({ abi: oneOfOneImageAbi, functionName: 'lockTokenField', args: [id, encodeTag(args.field)] }),
302
+ value: ZERO_VALUE,
303
+ chainId: args.chainId,
304
+ summary: `Lock token #${id} field "${args.field}" — permanent`,
305
+ fields: { contract: args.contract, tokenId: id.toString(), field: args.field },
306
+ };
307
+ }
308
+ /** Set/replace a collection (contract-wide) on-chain metadata field. Signer = owner. */
309
+ export function prepareSetContractField(args) {
310
+ return {
311
+ op: 'set-collection-field',
312
+ to: args.contract,
313
+ data: encodeFunctionData({
314
+ abi: oneOfOneImageAbi,
315
+ functionName: 'setContractField',
316
+ args: [encodeTag(args.field), encodeTag(args.representation), args.value],
317
+ }),
318
+ value: ZERO_VALUE,
319
+ chainId: args.chainId,
320
+ summary: `Set on-chain collection ${args.field} (${args.representation})`,
321
+ fields: { contract: args.contract, field: args.field, representation: args.representation, value: args.value },
322
+ };
323
+ }
324
+ /** Freeze a collection field forever (irreversible). Signer = owner. */
325
+ export function prepareLockContractField(args) {
326
+ return {
327
+ op: 'lock-collection-field',
328
+ to: args.contract,
329
+ data: encodeFunctionData({ abi: oneOfOneImageAbi, functionName: 'lockContractField', args: [encodeTag(args.field)] }),
330
+ value: ZERO_VALUE,
331
+ chainId: args.chainId,
332
+ summary: `Lock collection field "${args.field}" — permanent`,
333
+ fields: { contract: args.contract, field: args.field },
334
+ };
335
+ }
336
+ /** Transfer contract ownership (admin). Signer must be the current owner. */
337
+ export function prepareTransferOwnership(args) {
338
+ return {
339
+ op: 'set-admin',
340
+ to: args.contract,
341
+ data: encodeFunctionData({ abi: oneOfOneImageAbi, functionName: 'transferOwnership', args: [args.newOwner] }),
342
+ value: ZERO_VALUE,
343
+ chainId: args.chainId,
344
+ summary: `Transfer contract ownership → ${short(args.newOwner)}`,
345
+ fields: { contract: args.contract, newOwner: args.newOwner },
346
+ };
347
+ }
348
+ /**
349
+ * The unsigned form of the genesis deploy: a `deployDeterministic(params, salt)`
350
+ * call on the factory. The clone address is deterministic in (factory, salt) — a
351
+ * pure function of the salt, independent of the signer — so the URIs in `params`
352
+ * can be baked to point at the predicted address before anyone connects (see
353
+ * `predictClone`). `params.mintTo` decides whether the token mints in this tx or
354
+ * is deferred to a later `mint`. Building the URIs is the caller's job.
355
+ */
356
+ export function prepareDeployOneOfOne(args) {
357
+ return {
358
+ op: 'deploy',
359
+ to: args.factory,
360
+ data: encodeFunctionData({
361
+ abi: oneOfOneImageFactoryAbi,
362
+ functionName: 'deployDeterministic',
363
+ args: [args.params, args.salt],
364
+ }),
365
+ value: ZERO_VALUE,
366
+ chainId: args.chainId,
367
+ summary: `Deploy "${args.params.name}" → ${args.clone}`,
368
+ fields: {
369
+ factory: args.factory,
370
+ clone: args.clone,
371
+ name: args.params.name,
372
+ symbol: args.params.symbol,
373
+ owner: args.params.owner,
374
+ },
375
+ };
376
+ }
377
+ /** The unsigned form of the factory (trust-anchor) deploy — a contract creation. */
378
+ export function prepareDeployFactory(args) {
379
+ return {
380
+ op: 'deploy-factory',
381
+ to: null,
382
+ data: oneOfOneImageFactoryBytecode,
383
+ value: ZERO_VALUE,
384
+ chainId: args.chainId,
385
+ summary: 'Deploy the canonical clone factory (trust anchor)',
386
+ fields: {},
387
+ };
388
+ }
389
+ /** The unsigned form of the canonical metadata-renderer deploy — a contract creation. */
390
+ export function prepareDeployRenderer(args) {
391
+ return {
392
+ op: 'deploy-renderer',
393
+ to: null,
394
+ data: abxMetadataRendererBytecode,
395
+ value: ZERO_VALUE,
396
+ chainId: args.chainId,
397
+ summary: 'Deploy the canonical on-chain metadata renderer',
398
+ fields: {},
399
+ };
400
+ }
401
+ // ── Series (multi-token) ops ──────────────────────────────────────────────────
402
+ // Token ids are sequential and a token's metadata is its token id (no decoupling). Signer
403
+ // must be the owner or an authorized minter for the mint ops; owner-only for the config ops.
404
+ /**
405
+ * Mint the next sequential token on a Series (its metadata is its token id). Reverts when
406
+ * sold out.
407
+ */
408
+ export function prepareSeriesMint(args) {
409
+ return {
410
+ op: 'mint',
411
+ to: args.contract,
412
+ data: encodeFunctionData({ abi: seriesImageAbi, functionName: 'mint', args: [args.to] }),
413
+ value: ZERO_VALUE,
414
+ chainId: args.chainId,
415
+ summary: `Mint next token (in order) → ${short(args.to)}`,
416
+ fields: { contract: args.contract, to: args.to },
417
+ };
418
+ }
419
+ /** Mint `count` Series tokens in order in one tx — N individual `Transfer`s. */
420
+ export function prepareSeriesMintMany(args) {
421
+ const n = Number(args.count);
422
+ return {
423
+ op: 'mint-many',
424
+ to: args.contract,
425
+ data: encodeFunctionData({
426
+ abi: seriesImageAbi,
427
+ functionName: 'mintMany',
428
+ args: [args.to, BigInt(args.count)],
429
+ }),
430
+ value: ZERO_VALUE,
431
+ chainId: args.chainId,
432
+ summary: `Mint ${n} tokens (in order) → ${short(args.to)}`,
433
+ fields: { contract: args.contract, to: args.to, count: String(n) },
434
+ };
435
+ }
436
+ /**
437
+ * Set (or clear, with `0x0`) the single authorized minter. Setting a new minter atomically
438
+ * replaces any previous one; `0x0` clears it (back to owner-only). Signer must be the owner.
439
+ */
440
+ export function prepareSetMinter(args) {
441
+ const clearing = args.minter === zeroAddress;
442
+ return {
443
+ op: 'set-minter',
444
+ to: args.contract,
445
+ data: encodeFunctionData({ abi: seriesImageAbi, functionName: 'setMinter', args: [args.minter] }),
446
+ value: ZERO_VALUE,
447
+ chainId: args.chainId,
448
+ summary: clearing ? 'Clear the authorized minter (owner-only)' : `Set authorized minter → ${short(args.minter)}`,
449
+ fields: { contract: args.contract, minter: args.minter },
450
+ };
451
+ }
452
+ /** Lower the supply cap (monotonic — never raises, never below the minted count). Signer = owner. */
453
+ export function prepareSetMaxInvocations(args) {
454
+ const max = BigInt(args.maxInvocations);
455
+ return {
456
+ op: 'set-max-invocations',
457
+ to: args.contract,
458
+ data: encodeFunctionData({ abi: seriesImageAbi, functionName: 'setMaxInvocations', args: [max] }),
459
+ value: ZERO_VALUE,
460
+ chainId: args.chainId,
461
+ summary: `Set max invocations → ${max}`,
462
+ fields: { contract: args.contract, maxInvocations: max.toString() },
463
+ };
464
+ }
465
+ /** Pause or unpause minting. While paused only the owner may mint (reserves/config); unpausing
466
+ * opens it to the authorized minter. Signer must be the owner. */
467
+ export function prepareSetPaused(args) {
468
+ return {
469
+ op: 'set-paused',
470
+ to: args.contract,
471
+ data: encodeFunctionData({ abi: seriesImageAbi, functionName: 'setPaused', args: [args.paused] }),
472
+ value: ZERO_VALUE,
473
+ chainId: args.chainId,
474
+ summary: args.paused ? 'Pause minting (owner-only until unpaused)' : 'Unpause minting (open to the minter)',
475
+ fields: { contract: args.contract, paused: String(args.paused) },
476
+ };
477
+ }
478
+ /** Set (or clear, with `0x0`) the primary-sale payout destination. Signer = owner. */
479
+ export function prepareSetPrimaryPayee(args) {
480
+ return {
481
+ op: 'set-primary-payee',
482
+ to: args.contract,
483
+ data: encodeFunctionData({ abi: seriesImageAbi, functionName: 'setPrimaryPayee', args: [args.payee] }),
484
+ value: ZERO_VALUE,
485
+ chainId: args.chainId,
486
+ summary: args.payee !== zeroAddress ? `Set primary payee → ${short(args.payee)}` : 'Clear primary payee',
487
+ fields: { contract: args.contract, primaryPayee: args.payee },
488
+ };
489
+ }
490
+ /** The unsigned form of a Series genesis deploy — `deployDeterministic(params, salt)` on the Series factory. */
491
+ export function prepareDeploySeries(args) {
492
+ return {
493
+ op: 'deploy-series',
494
+ to: args.factory,
495
+ data: encodeFunctionData({
496
+ abi: seriesImageFactoryAbi,
497
+ functionName: 'deployDeterministic',
498
+ args: [args.params, args.salt],
499
+ }),
500
+ value: ZERO_VALUE,
501
+ chainId: args.chainId,
502
+ summary: `Deploy Series "${args.params.name}" (${String(args.params.maxInvocations)} max) → ${args.clone}`,
503
+ fields: {
504
+ factory: args.factory,
505
+ clone: args.clone,
506
+ name: args.params.name,
507
+ symbol: args.params.symbol,
508
+ owner: args.params.owner,
509
+ maxInvocations: String(args.params.maxInvocations),
510
+ },
511
+ };
512
+ }
513
+ /** The unsigned form of the Series factory (trust-anchor) deploy — a contract creation. */
514
+ export function prepareDeploySeriesFactory(args) {
515
+ return {
516
+ op: 'deploy-series-factory',
517
+ to: null,
518
+ data: seriesImageFactoryBytecode,
519
+ value: ZERO_VALUE,
520
+ chainId: args.chainId,
521
+ summary: 'Deploy the canonical Series clone factory (trust anchor)',
522
+ fields: {},
523
+ };
524
+ }
525
+ // ── Fixed-price minter (the Minter spine) ─────────────────────────────────────
526
+ // The minter is a shared, ownerless, multi-tenant singleton. `configure` defers to the
527
+ // token owner; `purchase`/`purchaseTo` are public. See specs/protocol/minter-spine.md.
528
+ /**
529
+ * Set/update a project's fixed-price sale on the shared minter. Signer must be the ABX
530
+ * token's owner. `paymentToken == 0x0` prices the sale in ETH; otherwise it's that ERC-20.
531
+ * `price` is raw units per token; `allocation` is the max this minter may sell (its budget,
532
+ * distinct from the token's own `maxInvocations` cap). Enabling the sale is separate from
533
+ * granting mint rights — also `prepareSetMinter(token, minter)`.
534
+ */
535
+ export function prepareConfigureSale(args) {
536
+ const paymentToken = args.paymentToken ?? zeroAddress;
537
+ const price = BigInt(args.price);
538
+ const allocation = BigInt(args.allocation);
539
+ const isEth = paymentToken === zeroAddress;
540
+ return {
541
+ op: 'configure-sale',
542
+ to: args.minter,
543
+ data: encodeFunctionData({
544
+ abi: abxFixedPriceMinterAbi,
545
+ functionName: 'configure',
546
+ args: [args.token, paymentToken, price, allocation],
547
+ }),
548
+ value: ZERO_VALUE,
549
+ chainId: args.chainId,
550
+ summary: `Configure sale: ${price} ${isEth ? 'wei' : `of ${short(paymentToken)}`}/token, allocation ${allocation}`,
551
+ fields: {
552
+ minter: args.minter,
553
+ token: args.token,
554
+ paymentToken,
555
+ price: price.toString(),
556
+ allocation: allocation.toString(),
557
+ },
558
+ };
559
+ }
560
+ /**
561
+ * Buy one token from the shared minter. `to` omitted → minted to the signer (`purchase`);
562
+ * `to` set → minted to that address (`purchaseTo`). `value` is the ETH to attach (equal to
563
+ * the sale price for an ETH sale; `0` for an ERC-20 sale, where the buyer must have approved
564
+ * the minter to pull `price` beforehand). Public — no owner rights needed.
565
+ */
566
+ export function preparePurchase(args) {
567
+ const value = BigInt(args.value ?? 0n);
568
+ const toRecipient = args.to && args.to !== zeroAddress;
569
+ return {
570
+ op: 'purchase',
571
+ to: args.minter,
572
+ data: toRecipient
573
+ ? encodeFunctionData({ abi: abxFixedPriceMinterAbi, functionName: 'purchaseTo', args: [args.token, args.to] })
574
+ : encodeFunctionData({ abi: abxFixedPriceMinterAbi, functionName: 'purchase', args: [args.token] }),
575
+ value: value > 0n ? toHex(value) : ZERO_VALUE,
576
+ chainId: args.chainId,
577
+ summary: toRecipient
578
+ ? `Buy 1 token → ${short(args.to)}${value > 0n ? ` for ${value} wei` : ''}`
579
+ : `Buy 1 token${value > 0n ? ` for ${value} wei` : ''}`,
580
+ fields: { minter: args.minter, token: args.token, to: args.to ?? '(sender)', value: value.toString() },
581
+ };
582
+ }
583
+ /** The unsigned form of the shared fixed-price minter deploy — a contract creation. */
584
+ export function prepareDeployFixedPriceMinter(args) {
585
+ return {
586
+ op: 'deploy-fixed-price-minter',
587
+ to: null,
588
+ data: abxFixedPriceMinterBytecode,
589
+ value: ZERO_VALUE,
590
+ chainId: args.chainId,
591
+ summary: 'Deploy the canonical fixed-price minter (shared, ownerless)',
592
+ fields: {},
593
+ };
594
+ }
595
+ /** Deterministic deploy of a {SeriesCode} clone — the code-project factory op. */
596
+ export function prepareDeploySeriesCode(args) {
597
+ return {
598
+ op: 'deploy-code',
599
+ to: args.factory,
600
+ data: encodeFunctionData({
601
+ abi: seriesCodeFactoryAbi,
602
+ functionName: 'deployDeterministic',
603
+ args: [args.params, args.salt],
604
+ }),
605
+ value: ZERO_VALUE,
606
+ chainId: args.chainId,
607
+ summary: `Deploy code project "${args.params.name}" (${String(args.params.maxInvocations)} max) → ${args.clone}`,
608
+ fields: {
609
+ factory: args.factory,
610
+ clone: args.clone,
611
+ name: args.params.name,
612
+ symbol: args.params.symbol,
613
+ owner: args.params.owner,
614
+ seedSource: args.params.seedSource,
615
+ maxInvocations: String(args.params.maxInvocations),
616
+ },
617
+ };
618
+ }
619
+ /** The code project's post-deploy setup: script chunks + PostParam schemas (+ dependency
620
+ * declarations, when the project has any; + the on-chain-URI legs under `--onchain-uri`),
621
+ * one atomic multicall. */
622
+ export function prepareCodeSetup(args) {
623
+ const deps = args.deps ?? [];
624
+ return {
625
+ op: 'code-setup',
626
+ to: args.contract,
627
+ data: encodeFunctionData({ abi: seriesCodeAbi, functionName: 'multicall', args: [args.calls] }),
628
+ value: ZERO_VALUE,
629
+ chainId: args.chainId,
630
+ // Human, and ACCURATE to what THIS multicall actually contains — a creator signs it in their
631
+ // wallet. (The old label was protocol jargon AND wrong for the renderer-only lane: it always
632
+ // claimed a "generator animation field" even when no generator/animation was in the tx.)
633
+ summary: (() => {
634
+ const parts = [];
635
+ if (args.chunkCount)
636
+ parts.push(`store your program (${args.chunkCount} on-chain chunk${args.chunkCount === 1 ? '' : 's'})`);
637
+ if (args.schemaKeys.length)
638
+ parts.push(`enable collector-set parameter${args.schemaKeys.length === 1 ? '' : 's'}: ${args.schemaKeys.join(', ')}`);
639
+ if (deps.length)
640
+ parts.push(`declare ${deps.length} dependenc${deps.length === 1 ? 'y' : 'ies'}: ${deps.join(', ')}`);
641
+ if (args.onchainUri)
642
+ parts.push('wire on-chain metadata resolution (the animation + URI renderers)');
643
+ return parts.length ? `Finish setup — ${parts.join('; ')}` : 'Finish setup';
644
+ })(),
645
+ fields: {
646
+ contract: args.contract,
647
+ scriptChunks: String(args.chunkCount),
648
+ schemas: args.schemaKeys.join(',') || '—',
649
+ ...(deps.length ? { dependencies: deps.join(',') } : {}),
650
+ ...(args.onchainUri ? { onchainUri: 'yes' } : {}),
651
+ },
652
+ };
653
+ }
654
+ // ── dependencies (Dependencies extension — code projects) ─────────────────────
655
+ // The ordered library declarations of a template-mode code project (index 0 = the runtime,
656
+ // by convention). Owner-only; the list stays dense (set at index ≤ count, remove only the
657
+ // last). See specs/protocol/dependency-registry.md + src/deps.ts for the ref encoding.
658
+ /** Set/replace the dependency at `index` — `resolution` 0 = Registry (`ref` = readable
659
+ * `name@version` bytes32), 1 = OnChain (`ref` = a data-contract address, left-aligned).
660
+ * Encode the pair with `parseDependencyRef`. Signer must be the owner. */
661
+ export function prepareSetDependency(args) {
662
+ const index = BigInt(args.index);
663
+ const kind = args.resolution === 1 ? 'on-chain' : 'registry';
664
+ const shown = args.display ?? args.ref;
665
+ return {
666
+ op: 'set-dependency',
667
+ to: args.contract,
668
+ data: encodeFunctionData({
669
+ abi: seriesCodeAbi,
670
+ functionName: 'setDependency',
671
+ args: [index, args.resolution, args.ref],
672
+ }),
673
+ value: ZERO_VALUE,
674
+ chainId: args.chainId,
675
+ summary: `Set dependency [${index}] → ${shown} (${kind})${index === 0n ? ' — index 0 = the runtime' : ''}`,
676
+ fields: { contract: args.contract, index: index.toString(), resolution: kind, ref: shown },
677
+ };
678
+ }
679
+ /** Remove the LAST dependency (the list stays dense — order is load-bearing). Signer = owner. */
680
+ export function prepareRemoveLastDependency(args) {
681
+ return {
682
+ op: 'remove-last-dependency',
683
+ to: args.contract,
684
+ data: encodeFunctionData({ abi: seriesCodeAbi, functionName: 'removeLastDependency', args: [] }),
685
+ value: ZERO_VALUE,
686
+ chainId: args.chainId,
687
+ summary: 'Remove the last dependency (the list stays dense)',
688
+ fields: { contract: args.contract },
689
+ };
690
+ }
691
+ /** Set (or clear, with `0x0`) the soft, non-validating dependency registry pointer —
692
+ * what `name@version` refs resolve through. Signer must be the owner. */
693
+ export function prepareSetDependencyRegistry(args) {
694
+ const clearing = args.registry === zeroAddress;
695
+ return {
696
+ op: 'set-dependency-registry',
697
+ to: args.contract,
698
+ data: encodeFunctionData({ abi: seriesCodeAbi, functionName: 'setDependencyRegistry', args: [args.registry] }),
699
+ value: ZERO_VALUE,
700
+ chainId: args.chainId,
701
+ summary: clearing
702
+ ? 'Clear the dependency registry pointer (soft — refs stay declared)'
703
+ : `Set dependency registry → ${short(args.registry)} (soft, non-validating)`,
704
+ fields: { contract: args.contract, registry: args.registry },
705
+ };
706
+ }
707
+ /** Freeze the dependency set (list + registry pointer) forever. Irreversible. Signer = owner. */
708
+ export function prepareLockDependencies(args) {
709
+ return {
710
+ op: 'lock-dependencies',
711
+ to: args.contract,
712
+ data: encodeFunctionData({ abi: seriesCodeAbi, functionName: 'lockDependencies', args: [] }),
713
+ value: ZERO_VALUE,
714
+ chainId: args.chainId,
715
+ summary: 'Freeze the dependency set (list + registry pointer) — permanent',
716
+ fields: { contract: args.contract },
717
+ };
718
+ }
719
+ /** Set a CONTRACT-scope param to a literal `bytes32` (the raw owner setter — Params base).
720
+ * Only for schema-less keys (a schema'd key closes the raw path: `SchemaGoverned`). This is
721
+ * the write behind well-known contract params like `params.keys` / `display.gateway`.
722
+ * Signer must be the owner. */
723
+ export function prepareSetContractParam(args) {
724
+ return {
725
+ op: 'set-contract-param',
726
+ to: args.contract,
727
+ data: encodeFunctionData({
728
+ abi: seriesCodeAbi,
729
+ functionName: 'setContractParam',
730
+ args: [encodeTag(args.key), args.value],
731
+ }),
732
+ value: ZERO_VALUE,
733
+ chainId: args.chainId,
734
+ summary: `Set contract param ${args.key} = ${args.display ?? args.value}`,
735
+ fields: { contract: args.contract, key: args.key, value: args.display ?? args.value },
736
+ };
737
+ }
738
+ /** Set a CONTRACT-scope param via the data path (one blob; the evented value is its keccak256).
739
+ * The raw owner setter for schema-less keys — the long-form twin of {@link prepareSetContractParam}. */
740
+ export function prepareSetContractParamData(args) {
741
+ return {
742
+ op: 'set-contract-param-data',
743
+ to: args.contract,
744
+ data: encodeFunctionData({
745
+ abi: seriesCodeAbi,
746
+ functionName: 'setContractParamData',
747
+ args: [encodeTag(args.key), args.data],
748
+ }),
749
+ value: ZERO_VALUE,
750
+ chainId: args.chainId,
751
+ summary: `Set contract param ${args.key} (data, ${(args.data.length - 2) / 2} bytes)`,
752
+ fields: { contract: args.contract, key: args.key },
753
+ };
754
+ }
755
+ /** Wire the three param-lifecycle hook addresses (configure/augment/transfer) in ONE owner call.
756
+ * The contract has no per-hook setter — `setParamHooks` writes all three at once — so a caller
757
+ * changing one hook must pass the CURRENT values for the other two (read them via `paramHooks()`),
758
+ * else it silently clears them. `zeroAddress` for any role means "no hook". SeriesCode only. */
759
+ export function prepareSetParamHooks(args) {
760
+ return {
761
+ op: 'set-param-hooks',
762
+ to: args.contract,
763
+ data: encodeFunctionData({
764
+ abi: seriesCodeAbi,
765
+ functionName: 'setParamHooks',
766
+ args: [args.configureHook, args.augmentHook, args.transferHook],
767
+ }),
768
+ value: ZERO_VALUE,
769
+ chainId: args.chainId,
770
+ summary: `Set param hooks — configure ${args.configureHook}, augment ${args.augmentHook}, transfer ${args.transferHook}`,
771
+ fields: {
772
+ contract: args.contract,
773
+ configureHook: args.configureHook,
774
+ augmentHook: args.augmentHook,
775
+ transferHook: args.transferHook,
776
+ },
777
+ };
778
+ }
779
+ /** Set a schema-governed PostParam (literal scalar) — signer must satisfy the schema's auth. */
780
+ export function prepareConfigureTokenParam(args) {
781
+ return {
782
+ op: 'configure-param',
783
+ to: args.contract,
784
+ data: encodeFunctionData({
785
+ abi: seriesCodeAbi,
786
+ functionName: 'configureTokenParam',
787
+ args: [args.tokenId, encodeTag(args.key), args.value],
788
+ }),
789
+ value: ZERO_VALUE,
790
+ chainId: args.chainId,
791
+ summary: `Configure ${args.key} = ${args.display} on token ${args.tokenId}`,
792
+ fields: { contract: args.contract, tokenId: String(args.tokenId), key: args.key, value: args.display },
793
+ };
794
+ }
795
+ /** Set a schema-governed `String`/`Bytes` PostParam (the data path — one blob, hash evented). */
796
+ export function prepareConfigureTokenParamData(args) {
797
+ return {
798
+ op: 'configure-param-data',
799
+ to: args.contract,
800
+ data: encodeFunctionData({
801
+ abi: seriesCodeAbi,
802
+ functionName: 'configureTokenParamData',
803
+ args: [args.tokenId, encodeTag(args.key), args.data],
804
+ }),
805
+ value: ZERO_VALUE,
806
+ chainId: args.chainId,
807
+ summary: `Configure ${args.key} (data, ${(args.data.length - 2) / 2} bytes) on token ${args.tokenId}`,
808
+ fields: { contract: args.contract, tokenId: String(args.tokenId), key: args.key },
809
+ };
810
+ }
811
+ //# sourceMappingURL=ops.js.map