@artblocks/abx-cli 0.1.0-alpha.16 → 0.1.0-alpha.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +233 -0
- package/dist/jsonout.d.ts +37 -0
- package/dist/jsonout.d.ts.map +1 -0
- package/dist/jsonout.js +68 -0
- package/dist/jsonout.js.map +1 -0
- package/dist/main.js +622 -18
- package/dist/main.js.map +1 -1
- package/dist/ownerops.d.ts +14 -0
- package/dist/ownerops.d.ts.map +1 -1
- package/dist/ownerops.js +164 -41
- package/dist/ownerops.js.map +1 -1
- package/dist/resume.d.ts +96 -0
- package/dist/resume.d.ts.map +1 -0
- package/dist/resume.js +95 -0
- package/dist/resume.js.map +1 -0
- package/dist/served.d.ts +46 -0
- package/dist/served.d.ts.map +1 -0
- package/dist/served.js +65 -0
- package/dist/served.js.map +1 -0
- package/package.json +6 -6
- package/skill/SKILL.md +3 -3
- package/skill/reference/code-projects.md +4 -3
- package/skill/reference/setup.md +2 -1
- package/skill/reference/troubleshooting.md +13 -0
package/dist/ownerops.d.ts
CHANGED
|
@@ -222,6 +222,20 @@ export declare function cmdSetField(address: string | undefined, flags: Flags):
|
|
|
222
222
|
* - computed keys (`artifacts`, `abx_provenance`) are refused — they're the manifest, not inputs.
|
|
223
223
|
* Scope: token (`--token`, default 0) or `--collection`. Any signing lane. Owner-only.
|
|
224
224
|
*/
|
|
225
|
+
/**
|
|
226
|
+
* Attach one or more artifacts. Several `<key> <uri>` pairs in one invocation become **one
|
|
227
|
+
* transaction**.
|
|
228
|
+
*
|
|
229
|
+
* That batching is a safety fix, not a convenience (backlog B20). The CLI's most-documented flow —
|
|
230
|
+
* mint, then attach each artifact, then refresh — sent one transaction per step with no all-or-nothing
|
|
231
|
+
* boundary, so a failure partway through left a permanently half-written token that cannot be
|
|
232
|
+
* un-minted. An integrator hit exactly this and folded 8 operations into 1 transaction using the SDK's
|
|
233
|
+
* `batchOps`, which the CLI already shipped and did not use. Now it does: N pairs are one
|
|
234
|
+
* `multicall`, so either every artifact lands or none does.
|
|
235
|
+
*
|
|
236
|
+
* A single pair passes through `batchOps` untouched, so the one-artifact case sends the identical
|
|
237
|
+
* transaction it always did.
|
|
238
|
+
*/
|
|
225
239
|
export declare function cmdAttach(rest: string[], flags: Flags): Promise<void>;
|
|
226
240
|
export declare function cmdLockField(address: string | undefined, flags: Flags): Promise<void>;
|
|
227
241
|
/** Point a token's URI resolution at an on-chain renderer (or clear it with `--off`).
|
package/dist/ownerops.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ownerops.d.ts","sourceRoot":"","sources":["../src/ownerops.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAeL,cAAc,EAmBd,KAAK,kBAAkB,
|
|
1
|
+
{"version":3,"file":"ownerops.d.ts","sourceRoot":"","sources":["../src/ownerops.ts"],"names":[],"mappings":"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"}
|
package/dist/ownerops.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* served state reflects the change. The agent picks the lane; the human only
|
|
7
7
|
* approves (wallet lane) or it's the env key (hot lane).
|
|
8
8
|
*/
|
|
9
|
-
import { assertChainId, ensureChunkStore as sdkEnsureChunkStore, predictFixedPriceMinter, encodeReader, makePublicClient, makeWalletClient, oneOfOneImageAbi, seriesImageAbi, abxFixedPriceMinterAbi, deployFixedPriceMinter, prepareConfigureSale, preparePurchase, planChunks, planContentTxs, prepareLockContractField, prepareLockContractURI, prepareLockTokenField, prepareLockTokenURI, prepareMint, prepareSeriesMintMany, prepareSetMinter, prepareSetMaxInvocations, prepareSetPrimaryPayee, prepareSetPaused, prepareSetContractField, prepareSetContractURIBase, prepareSetContractURIOverride, prepareSetContractURIRenderer, prepareSetParamHooks, prepareSetParamSchema, prepareRetireParam, readParamSchema, PARAM_TYPES, prepareSetRoyalty, prepareSetTokenField, prepareSetTokenURIBase, prepareSetTokenURIOverride, prepareSetTokenURIRenderer, prepareSetTransferValidator, prepareTransfer, prepareTransferOwnership, readCreatorTokenStatus, resolveRecommendedTransferValidator, RECOMMENDED_TRANSFER_VALIDATOR, KNOWN_CHAIN_KEYS, resolveChain, resolveRpcUrl, redactRpcUrl, DEFAULT_CHAIN_KEY, stageContent, encodeTag, METADATA_FIELD as F, METADATA_REPRESENTATION as R, } from '@artblocks/abx-sdk';
|
|
9
|
+
import { assertChainId, ensureChunkStore as sdkEnsureChunkStore, predictFixedPriceMinter, encodeReader, makePublicClient, makeWalletClient, oneOfOneImageAbi, seriesImageAbi, abxFixedPriceMinterAbi, deployFixedPriceMinter, prepareConfigureSale, preparePurchase, planChunks, planContentTxs, prepareLockContractField, prepareLockContractURI, prepareLockTokenField, prepareLockTokenURI, prepareMint, prepareSeriesMintMany, prepareSetMinter, prepareSetMaxInvocations, prepareSetPrimaryPayee, prepareSetPaused, prepareSetContractField, prepareSetContractURIBase, prepareSetContractURIOverride, prepareSetContractURIRenderer, prepareSetParamHooks, prepareSetParamSchema, prepareRetireParam, readParamSchema, PARAM_TYPES, prepareSetRoyalty, prepareSetTokenField, batchOps, prepareSetTokenURIBase, prepareSetTokenURIOverride, prepareSetTokenURIRenderer, prepareSetTransferValidator, prepareTransfer, prepareTransferOwnership, readCreatorTokenStatus, resolveRecommendedTransferValidator, RECOMMENDED_TRANSFER_VALIDATOR, KNOWN_CHAIN_KEYS, resolveChain, resolveRpcUrl, redactRpcUrl, DEFAULT_CHAIN_KEY, stageContent, encodeTag, METADATA_FIELD as F, METADATA_REPRESENTATION as R, } from '@artblocks/abx-sdk';
|
|
10
10
|
import { SelfHostIndexer } from '@artblocks/abx-indexer';
|
|
11
11
|
import { encodeScalarParam, encodeTag as encodeTagSdk, isAccepted, prepareConfigureTokenParam, prepareConfigureTokenParamData, prepareSetContractParam, prepareSetContractParamData, seriesCodeAbi, } from '@artblocks/abx-sdk';
|
|
12
12
|
import { hasParamEnumeration } from './onchain-uri.js';
|
|
@@ -16,9 +16,10 @@ import { toHex as toHexSdk } from 'viem';
|
|
|
16
16
|
import { readFileSync } from 'node:fs';
|
|
17
17
|
import { basename, resolve as resolvePath } from 'node:path';
|
|
18
18
|
import { gzipSync } from 'node:zlib';
|
|
19
|
-
import { formatEther, getAddress, isAddress, parseEther, toHex, zeroAddress } from 'viem';
|
|
19
|
+
import { decodeEventLog, formatEther, getAddress, isAddress, parseEther, toHex, zeroAddress } from 'viem';
|
|
20
20
|
import { fixedPriceMinterAddress } from './config.js';
|
|
21
21
|
import { openWalletSession, signTx } from './signer.js';
|
|
22
|
+
import { withJson } from './jsonout.js';
|
|
22
23
|
import { resolveRemote, serviceClient } from './remote.js';
|
|
23
24
|
import { positionalArgs, unknownFlags } from './flags.js';
|
|
24
25
|
import { parseSchemaSpecs, describeSchema } from './schema.js';
|
|
@@ -99,6 +100,15 @@ function requireFlag(flags, name, usage) {
|
|
|
99
100
|
}
|
|
100
101
|
/** Run a prepared write through the chosen lane, then re-index if the project is known.
|
|
101
102
|
* Returns whether anything was BROADCAST (false on the cold lane — the tx was only printed). */
|
|
103
|
+
/**
|
|
104
|
+
* Every owner-op's send choke point. Returns the {@link SignResult} when a tx was broadcast, else
|
|
105
|
+
* `null` (dry run, or the cold lane which prints a tx instead of sending one).
|
|
106
|
+
*
|
|
107
|
+
* It used to return a bare boolean. It returns the result now because a caller sometimes needs the
|
|
108
|
+
* *receipt* — `abx mint --json` has to report which token id was actually minted, and the only
|
|
109
|
+
* authoritative answer is the Transfer log the mint emitted. Callers that only asked "did it send?"
|
|
110
|
+
* keep working: `null` is falsy, and a result object is truthy.
|
|
111
|
+
*/
|
|
102
112
|
async function runWrite(address, provider, flags, expectedSigner) {
|
|
103
113
|
// --dry-run: preview the exact tx and send NOTHING. This is the shared choke point for every
|
|
104
114
|
// owner-op, so --dry-run is uniform across them — it must never fall through to a real send just
|
|
@@ -113,7 +123,7 @@ async function runWrite(address, provider, flags, expectedSigner) {
|
|
|
113
123
|
if (expectedSigner)
|
|
114
124
|
console.log(` ${dim('owner'.padEnd(12))} ${expectedSigner}`);
|
|
115
125
|
console.log(dim(`\n Re-run without --dry-run to send (lane: ${laneFromFlags(flags)}).\n`));
|
|
116
|
-
return
|
|
126
|
+
return null;
|
|
117
127
|
}
|
|
118
128
|
await assertChainId(CHAIN); // verify the RPC really is CHAIN before any irreversible write
|
|
119
129
|
const result = await signTx(provider, {
|
|
@@ -125,9 +135,9 @@ async function runWrite(address, provider, flags, expectedSigner) {
|
|
|
125
135
|
signUrlFile: flags['sign-url-file'],
|
|
126
136
|
});
|
|
127
137
|
if (!result)
|
|
128
|
-
return
|
|
138
|
+
return null; // cold lane — nothing broadcast
|
|
129
139
|
await reindexIfKnown(address, flags);
|
|
130
|
-
return
|
|
140
|
+
return result;
|
|
131
141
|
}
|
|
132
142
|
async function reindexIfKnown(address, flags) {
|
|
133
143
|
const indexer = new SelfHostIndexer();
|
|
@@ -487,24 +497,84 @@ export async function cmdLockDependencies(address, flags) {
|
|
|
487
497
|
// it mints the next sequential token id (metadata = token id): bare (one), or `--count <n>`
|
|
488
498
|
// (n in order). Owner or an authorized minter signs.
|
|
489
499
|
export async function cmdMint(address, flags) {
|
|
490
|
-
const usage = 'abx mint <address> [--to 0x…] [--count <n>] [--sign|--unsigned]';
|
|
500
|
+
const usage = 'abx mint <address> [--to 0x…] [--count <n>] [--json] [--sign|--unsigned]';
|
|
491
501
|
const contract = requireAddress(address, usage);
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
502
|
+
// `--json`: the TOKEN ID is the value a program came for, and it was previously only obtainable by
|
|
503
|
+
// regex-scraping coloured prose (B19). It is read from the mint's own Transfer logs rather than by
|
|
504
|
+
// re-reading `nextTokenId` afterwards — a concurrent mint would make that answer wrong, and a
|
|
505
|
+
// number that is usually right is worse than no number.
|
|
506
|
+
return withJson(flags, async () => {
|
|
507
|
+
const owner = await read(contract, 'owner');
|
|
508
|
+
const to = flags.to ?? owner; // default: pre-mint to the admin
|
|
509
|
+
let tx;
|
|
510
|
+
let count = 1n;
|
|
511
|
+
if (flags.count !== undefined && flags.count !== 'true') {
|
|
512
|
+
count = BigInt(flags.count);
|
|
513
|
+
console.log(dim(` minting ${count} Series tokens in order → ${to}`));
|
|
514
|
+
tx = prepareSeriesMintMany({ contract, to, count, chainId: chainId() });
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
// Bare mint: the `mint(address)` selector is shared by the 1/1 (token #0) and a
|
|
518
|
+
// Series (next sequential token) — one path serves both.
|
|
519
|
+
console.log(dim(` minting token → ${to}${flags.to ? '' : ' (owner — pass --to for a buyer)'}`));
|
|
520
|
+
tx = prepareMint({ contract, to, chainId: chainId() });
|
|
521
|
+
}
|
|
522
|
+
const result = await runWrite(contract, tx, flags, owner);
|
|
523
|
+
const tokenIds = result ? await mintedTokenIds(contract, result.txHash) : [];
|
|
524
|
+
if (result && tokenIds.length) {
|
|
525
|
+
console.log(` ${green('✓')} minted token${tokenIds.length > 1 ? 's' : ''} ${bold(tokenIds.map((t) => '#' + t).join(', '))} → ${to}`);
|
|
526
|
+
}
|
|
527
|
+
console.log(dim(` next: \`abx refresh ${contract}\` so marketplaces pick up the new token.`));
|
|
528
|
+
return {
|
|
529
|
+
contract,
|
|
530
|
+
chainId: chainId(),
|
|
531
|
+
to,
|
|
532
|
+
// `sent: false` is the dry-run and cold-lane answer — an empty tokenIds with no explanation
|
|
533
|
+
// would read as a failed mint.
|
|
534
|
+
sent: !!result,
|
|
535
|
+
txHash: result?.txHash ?? null,
|
|
536
|
+
blockNumber: result ? String(result.blockNumber) : null,
|
|
537
|
+
tokenIds,
|
|
538
|
+
requestedCount: Number(count),
|
|
539
|
+
};
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* The token ids a mint actually created, decoded from its receipt's `Transfer(from=0x0)` logs.
|
|
544
|
+
*
|
|
545
|
+
* Authoritative by construction: the ids come from the transaction that minted them, so a
|
|
546
|
+
* `--count N` batch reports all N and a concurrent mint elsewhere cannot skew the answer. Returns
|
|
547
|
+
* `[]` rather than throwing if the receipt can't be read — a mint that landed on-chain must not be
|
|
548
|
+
* reported as failed because a follow-up read hiccuped.
|
|
549
|
+
*/
|
|
550
|
+
async function mintedTokenIds(contract, txHash) {
|
|
551
|
+
try {
|
|
552
|
+
const publicClient = makePublicClient({ chainKey: CHAIN });
|
|
553
|
+
const receipt = await publicClient.getTransactionReceipt({ hash: txHash });
|
|
554
|
+
const ids = [];
|
|
555
|
+
for (const log of receipt.logs) {
|
|
556
|
+
if (log.address.toLowerCase() !== contract.toLowerCase())
|
|
557
|
+
continue;
|
|
558
|
+
try {
|
|
559
|
+
const decoded = decodeEventLog({ abi: oneOfOneImageAbi, data: log.data, topics: log.topics });
|
|
560
|
+
if (decoded.eventName !== 'Transfer')
|
|
561
|
+
continue;
|
|
562
|
+
const args = decoded.args;
|
|
563
|
+
if (args.from && args.from !== zeroAddress)
|
|
564
|
+
continue; // a transfer, not a mint
|
|
565
|
+
const id = args.id ?? args.tokenId;
|
|
566
|
+
if (id !== undefined)
|
|
567
|
+
ids.push(id.toString());
|
|
568
|
+
}
|
|
569
|
+
catch {
|
|
570
|
+
continue; // a log from another interface on the same contract
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return ids.sort((a, b) => Number(BigInt(a) - BigInt(b)));
|
|
499
574
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
// Series (next sequential token) — one path serves both.
|
|
503
|
-
console.log(dim(` minting token → ${to}${flags.to ? '' : ' (owner — pass --to for a buyer)'}`));
|
|
504
|
-
tx = prepareMint({ contract, to, chainId: chainId() });
|
|
575
|
+
catch {
|
|
576
|
+
return [];
|
|
505
577
|
}
|
|
506
|
-
await runWrite(contract, tx, flags, owner);
|
|
507
|
-
console.log(dim(` next: \`abx refresh ${contract}\` so marketplaces pick up the new token.`));
|
|
508
578
|
}
|
|
509
579
|
// ── series owner ops (minter set · supply cap · primary payee) ────────────────
|
|
510
580
|
// The multi-token knobs. All owner-only; each runs through the same signing lane +
|
|
@@ -1119,9 +1189,25 @@ export async function cmdSetField(address, flags) {
|
|
|
1119
1189
|
* - computed keys (`artifacts`, `abx_provenance`) are refused — they're the manifest, not inputs.
|
|
1120
1190
|
* Scope: token (`--token`, default 0) or `--collection`. Any signing lane. Owner-only.
|
|
1121
1191
|
*/
|
|
1192
|
+
/**
|
|
1193
|
+
* Attach one or more artifacts. Several `<key> <uri>` pairs in one invocation become **one
|
|
1194
|
+
* transaction**.
|
|
1195
|
+
*
|
|
1196
|
+
* That batching is a safety fix, not a convenience (backlog B20). The CLI's most-documented flow —
|
|
1197
|
+
* mint, then attach each artifact, then refresh — sent one transaction per step with no all-or-nothing
|
|
1198
|
+
* boundary, so a failure partway through left a permanently half-written token that cannot be
|
|
1199
|
+
* un-minted. An integrator hit exactly this and folded 8 operations into 1 transaction using the SDK's
|
|
1200
|
+
* `batchOps`, which the CLI already shipped and did not use. Now it does: N pairs are one
|
|
1201
|
+
* `multicall`, so either every artifact lands or none does.
|
|
1202
|
+
*
|
|
1203
|
+
* A single pair passes through `batchOps` untouched, so the one-artifact case sends the identical
|
|
1204
|
+
* transaction it always did.
|
|
1205
|
+
*/
|
|
1122
1206
|
export async function cmdAttach(rest, flags) {
|
|
1123
|
-
const usage = 'abx attach <address> <key> <ipfs://… | ar://… | https://…> [--file <path>] [--collection | --token 0] [--sign|--unsigned] [--dry-run]';
|
|
1124
|
-
const [address,
|
|
1207
|
+
const usage = 'abx attach <address> <key> <ipfs://… | ar://… | https://…> [<key> <uri> …] [--file <path>] [--collection | --token 0] [--sign|--unsigned] [--dry-run]';
|
|
1208
|
+
const [address, ...pairArgs] = positionalArgs(rest);
|
|
1209
|
+
const key = pairArgs[0];
|
|
1210
|
+
const uri = pairArgs[1];
|
|
1125
1211
|
// Warn (never throw) on an unrecognized flag — a typo or a hopeful `--mime-type` otherwise no-ops
|
|
1126
1212
|
// INVISIBLY (round-1: an agent passed `--mime-type` and it was silently swallowed). mimeType is
|
|
1127
1213
|
// declared from the URL extension, not a flag; say so.
|
|
@@ -1156,19 +1242,44 @@ export async function cmdAttach(rest, flags) {
|
|
|
1156
1242
|
// Locator path (the common case): auto-detect the representation, refuse an unrecognized scheme
|
|
1157
1243
|
// loudly (never silently store a bad value) — fail fast before any RPC. Derive the declared
|
|
1158
1244
|
// mimeType from the extension.
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1245
|
+
// Every `<key> <uri>` pair, validated BEFORE anything is sent — a batch that would revert partway
|
|
1246
|
+
// is exactly what this command now exists to prevent, so a bad locator in pair 5 must stop pair 1.
|
|
1247
|
+
if (pairArgs.length % 2 !== 0) {
|
|
1248
|
+
throw new Error(`attach takes <key> <uri> PAIRS; got ${pairArgs.length} positional argument(s) after the address. ` +
|
|
1249
|
+
`Last one seen: "${pairArgs[pairArgs.length - 1]}".`);
|
|
1250
|
+
}
|
|
1251
|
+
const pairs = [];
|
|
1252
|
+
for (let i = 0; i < pairArgs.length; i += 2) {
|
|
1253
|
+
const k = pairArgs[i];
|
|
1254
|
+
const u = pairArgs[i + 1];
|
|
1255
|
+
assertSettableField(k);
|
|
1256
|
+
const rep = representationForLocator(u);
|
|
1257
|
+
if (!rep) {
|
|
1258
|
+
throw new Error(`"${u}" isn't a recognized file locator. Use ipfs://… (pinned/IPFS), ar://… (Arweave), or https://… . ` +
|
|
1259
|
+
'To store literal text or raw bytes on-chain instead, use `abx set-field`.');
|
|
1260
|
+
}
|
|
1261
|
+
pairs.push({ key: k, uri: u, representation: rep, mimeType: contentTypeFromPath(u) });
|
|
1163
1262
|
}
|
|
1263
|
+
// A key repeated within one batch would have the later write silently win — the same
|
|
1264
|
+
// full-column-upsert hazard that bit `register` and `lock-field`. Refuse instead.
|
|
1265
|
+
const dupes = pairs.map((p) => p.key).filter((k, i, a) => a.indexOf(k) !== i);
|
|
1266
|
+
if (dupes.length) {
|
|
1267
|
+
throw new Error(`the same key appears twice in one batch: ${[...new Set(dupes)].join(', ')}. ` +
|
|
1268
|
+
'Each field holds ONE active value, so the last write would silently win — attach them separately if that is really what you want.');
|
|
1269
|
+
}
|
|
1270
|
+
const representation = pairs[0].representation;
|
|
1164
1271
|
const owner = await read(contract, 'owner');
|
|
1165
|
-
const
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1272
|
+
for (const p of pairs) {
|
|
1273
|
+
if (p.mimeType === 'application/octet-stream') {
|
|
1274
|
+
console.log(yellow(' ⚠ ') +
|
|
1275
|
+
dim(`no file extension in "${p.uri.slice(0, 64)}" → declared type will be application/octet-stream. `) +
|
|
1276
|
+
dim('Point the URI at the file itself (…/master.tiff, …/coa.pdf) so collectors get the right type.'));
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
const mimeType = pairs[0].mimeType;
|
|
1280
|
+
for (const p of pairs) {
|
|
1281
|
+
console.log(` attaching ${bold(p.key)} ${dim(`(${p.mimeType}, ${p.representation})`)} to ${scope}: ${dim(p.uri)}`);
|
|
1170
1282
|
}
|
|
1171
|
-
console.log(` attaching ${bold(key)} ${dim(`(${mimeType}, ${representation})`)} to ${scope}: ${dim(uri)}`);
|
|
1172
1283
|
// Where it surfaces. This used to be one dim line, and it read as a footnote rather than as a
|
|
1173
1284
|
// dependency: an integrator attached five audio stems to a fully-on-chain token, paid to store
|
|
1174
1285
|
// them, and found `tokenURI` listed none of them — "paid for, stored on-chain, and invisible".
|
|
@@ -1179,25 +1290,37 @@ export async function cmdAttach(rest, flags) {
|
|
|
1179
1290
|
const uriBase = await read(contract, 'tokenURIBase').catch(() => '');
|
|
1180
1291
|
const artifactsPath = `/t/${chainId()}/${contract}/${flags.token ?? '0'}`;
|
|
1181
1292
|
if (uriBase && uriBase.trim() !== '') {
|
|
1182
|
-
console.log(dim(` → listed in this project's resolver artifacts (${artifactsPath} and /data
|
|
1293
|
+
console.log(dim(` → listed in this project's resolver artifacts (${artifactsPath} and /data/<key>); a bare on-chain tokenURI carries reserved fields plus abx_params only.`));
|
|
1183
1294
|
}
|
|
1184
1295
|
else {
|
|
1185
1296
|
console.log(yellow(' ⚠ ') +
|
|
1186
1297
|
`this project resolves ON-CHAIN (no resolver base baked in), and the on-chain document carries reserved fields plus the computed ${bold('abx_params')} block only — ` +
|
|
1187
|
-
`so ${bold(key)} will NOT appear in ${bold('tokenURI')}. The bytes are stored and provable, but nothing surfaces them to a marketplace or wallet. ` +
|
|
1298
|
+
`so ${pairs.map((p) => bold(p.key)).join(', ')} will NOT appear in ${bold('tokenURI')}. The bytes are stored and provable, but nothing surfaces them to a marketplace or wallet. ` +
|
|
1188
1299
|
dim('(Configured params DO appear on-chain — attachments are the surface that needs a resolver.)'));
|
|
1189
1300
|
console.log(dim(` to make attached artifacts visible, point the project at a resolver (${bold('abx deploy-resolver')}, or a managed one via ${bold('abx add <addr> --remote <name>')}) — it serves the listing at ${artifactsPath}.`));
|
|
1190
1301
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1302
|
+
// ONE transaction for the whole set. `batchOps` folds a same-target run into a `multicall` and
|
|
1303
|
+
// passes a lone op through untouched, so a single attach is byte-identical to before.
|
|
1304
|
+
const ops = pairs.map((p) => collection
|
|
1305
|
+
? prepareSetContractField({ contract, field: p.key, representation: p.representation, value: toHex(p.uri), chainId: chainId() })
|
|
1306
|
+
: prepareSetTokenField({ contract, tokenId: BigInt(flags.token ?? '0'), field: p.key, representation: p.representation, value: toHex(p.uri), chainId: chainId() }));
|
|
1307
|
+
const batched = batchOps(ops);
|
|
1308
|
+
if (batched.length !== 1) {
|
|
1309
|
+
// Defensive: every op here targets the same contract and carries no value, so batchOps must
|
|
1310
|
+
// return exactly one tx. If that ever changes, fail loudly rather than send a partial set.
|
|
1311
|
+
throw new Error(`attach expected to batch ${ops.length} op(s) into one transaction, got ${batched.length}`);
|
|
1312
|
+
}
|
|
1313
|
+
if (pairs.length > 1) {
|
|
1314
|
+
console.log(dim(` ${pairs.length} artifacts → ONE transaction (all-or-nothing: a revert lands none of them, so no half-written token).`));
|
|
1315
|
+
}
|
|
1316
|
+
const sent = await runWrite(contract, batched[0], flags, owner);
|
|
1195
1317
|
if (sent) {
|
|
1196
1318
|
const id = flags.token ?? '0';
|
|
1197
|
-
|
|
1319
|
+
const names = pairs.map((p) => bold(p.key)).join(', ');
|
|
1320
|
+
console.log(` ${green('✓')} attached — ${names} join${pairs.length > 1 ? '' : 's'} ${scope}'s ${bold('artifacts')} manifest (stored on-chain, anchored).\n` +
|
|
1198
1321
|
dim(` verify (a resolver serves the complete listing): `) +
|
|
1199
|
-
`
|
|
1200
|
-
dim(` ${collection ? '' : `→ artifacts[].key "${key}"; /data
|
|
1322
|
+
`abx tokenuri ${contract}${id === '0' ? '' : ` --token ${id}`} --fetch` +
|
|
1323
|
+
dim(` ${collection ? '' : `→ artifacts[].key ${pairs.map((p) => `"${p.key}"`).join(', ')}; /data/<key> fetches each`}\n`) +
|
|
1201
1324
|
dim(` (The complete file listing is a resolver surface — the bare on-chain tokenURI enumerates reserved fields plus abx_params. Params never need a resolver; attachments do.)\n`));
|
|
1202
1325
|
}
|
|
1203
1326
|
}
|