@artblocks/abx-cli 0.1.0-alpha.14 → 0.1.0-alpha.15
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/dist/main.js +97 -10
- package/dist/main.js.map +1 -1
- package/dist/ownerops.d.ts +25 -0
- package/dist/ownerops.d.ts.map +1 -1
- package/dist/ownerops.js +107 -1
- package/dist/ownerops.js.map +1 -1
- package/package.json +6 -6
- package/skill/SKILL.md +2 -1
- package/skill/reference/creator-token.md +71 -0
package/dist/main.js
CHANGED
|
@@ -74,11 +74,11 @@ const SCHEMA_CATALOG = 'Types: Bool·Select·Uint256Range·Int256Range·DecimalR
|
|
|
74
74
|
'Select needs options — key:Select[A|B|C]:Auth; a Range takes bounds — key:Uint256Range[0..100]:Auth. ' +
|
|
75
75
|
'A palette collectors set = palette:HexColor:TokenOwner';
|
|
76
76
|
import { uploadAndLocate } from './upload.js';
|
|
77
|
-
import { assertChainId, discoverDeployBlock, deployFactory, deploySeriesFactory, deploySeries, deployOneOfOne, deployRenderer, predictRenderer, predictSeedSource, encodeTag, encodeFieldRenderer, isCodeProject, loadDotEnv, makePublicClient, makeWalletClient, oneOfOneImageAbi, oneOfOneImageFactoryAbi, seriesImageAbi, seriesImageFactoryAbi, abxMetadataRendererAbi, predictClone, probeRpcEndpoints, prepareDeployOneOfOne, prepareDeploySeries, reconstructProject, saltFor, saltGuard, resolveChain, resolveRpcUrl, redactRpcUrl, explorerUrl, DEFAULT_CHAIN_KEY, KNOWN_CHAIN_KEYS, normalizeAttributes, parseTraitPairs, METADATA_FIELD as F, METADATA_REPRESENTATION as R, indexProgress, isAccepted, AbxIndexTimeoutError, AbxServiceError, } from '@artblocks/abx-sdk';
|
|
77
|
+
import { assertChainId, discoverDeployBlock, deployFactory, deploySeriesFactory, deploySeries, deployOneOfOne, deployRenderer, predictRenderer, predictSeedSource, encodeTag, encodeFieldRenderer, isCodeProject, loadDotEnv, makePublicClient, makeWalletClient, oneOfOneImageAbi, oneOfOneImageFactoryAbi, seriesImageAbi, seriesImageFactoryAbi, abxMetadataRendererAbi, predictClone, probeRpcEndpoints, prepareDeployOneOfOne, prepareDeploySeries, reconstructProject, saltFor, saltGuard, resolveChain, resolveRpcUrl, redactRpcUrl, explorerUrl, DEFAULT_CHAIN_KEY, KNOWN_CHAIN_KEYS, normalizeAttributes, parseTraitPairs, resolveRecommendedTransferValidator, CREATOR_TOKEN_INTERFACE_ID, METADATA_FIELD as F, METADATA_REPRESENTATION as R, indexProgress, isAccepted, AbxIndexTimeoutError, AbxServiceError, } from '@artblocks/abx-sdk';
|
|
78
78
|
import { SelfHostIndexer, SqliteStore } from '@artblocks/abx-indexer';
|
|
79
79
|
import { artContentHash, currentRenderArtifact, generateArt, resolveBaseUrl, startChainWatcher, startTokenApiServer, verifyProject, watchIntervalMs, DEFAULT_PORT, } from '@artblocks/abx-token-api';
|
|
80
80
|
import { ARWEAVE_FREE_UPLOAD_LIMIT, arweaveAddress, arweaveFunding, contentTypeFromPath, hashContent, resolveBackend, turboBalanceForAddress, turboUploadCostUsd, turboUploadWinc } from '@artblocks/abx-storage';
|
|
81
|
-
import { cmdTransfer, cmdMint, cmdSetMinter, cmdSetMaxInvocations, cmdConfigureParam, cmdSetSchema, cmdRetireParam, cmdSetParamHooks, cmdSetDependency, cmdRemoveLastDependency, cmdSetDependencyRegistry, cmdLockDependencies, cmdSetPrimaryPayee, cmdPause, cmdUnpause, cmdRefresh, cmdSetTokenUri, cmdSetContractUri, cmdSetRoyalty, cmdSetField, cmdAttach, cmdLockField, cmdSetRenderer, cmdLockUri, cmdSetAdmin, cmdMinterConfigure, cmdMinterShow, cmdMinterBuy, computeContentPlan, envStagingSender, laneFromFlags, ONCHAIN_PROJECT_SOFT_LIMIT, parseCompress, previewImageStaging, sessionStagingSender, stageImageField, stageImageFieldsBatch, authorshipContractFields, AUTHORSHIP_DEPLOY_FIELDS, } from './ownerops.js';
|
|
81
|
+
import { cmdTransfer, cmdMint, cmdSetMinter, cmdSetMaxInvocations, cmdConfigureParam, cmdSetSchema, cmdRetireParam, cmdSetParamHooks, cmdSetDependency, cmdRemoveLastDependency, cmdSetDependencyRegistry, cmdLockDependencies, cmdSetPrimaryPayee, cmdPause, cmdUnpause, cmdRefresh, cmdSetTokenUri, cmdSetContractUri, cmdSetRoyalty, cmdSetTransferValidator, cmdSetField, cmdAttach, parseTransferValidatorValue, cmdLockField, cmdSetRenderer, cmdLockUri, cmdSetAdmin, cmdMinterConfigure, cmdMinterShow, cmdMinterBuy, computeContentPlan, envStagingSender, laneFromFlags, ONCHAIN_PROJECT_SOFT_LIMIT, parseCompress, previewImageStaging, sessionStagingSender, stageImageField, stageImageFieldsBatch, authorshipContractFields, AUTHORSHIP_DEPLOY_FIELDS, } from './ownerops.js';
|
|
82
82
|
import { openWalletSession, signTx } from './signer.js';
|
|
83
83
|
import { describeRemoteError, listConfiguredRemotes, misnamedRemoteVars, requireRemoteToken, resolveRemote, serviceClient, tokenSourceLabel } from './remote.js';
|
|
84
84
|
import { buildMigrationPlan, repinNodeCustody, verifyParity } from './migrate.js';
|
|
@@ -245,6 +245,7 @@ async function main() {
|
|
|
245
245
|
case 'deploy-effects': return cmdDeployEffects(flags);
|
|
246
246
|
case 'mint-page': return cmdMintPage(rest[0], flags);
|
|
247
247
|
case 'set-royalty': return cmdSetRoyalty(rest[0], flags);
|
|
248
|
+
case 'set-transfer-validator': return cmdSetTransferValidator(rest[0], rest.slice(1), flags);
|
|
248
249
|
case 'set-field': return cmdSetField(rest[0], flags);
|
|
249
250
|
case 'attach': return cmdAttach(rest, flags);
|
|
250
251
|
case 'lock-field': return cmdLockField(rest[0], flags);
|
|
@@ -445,6 +446,7 @@ function deployCommandLine(flags, salt) {
|
|
|
445
446
|
str('compress', flags.compress);
|
|
446
447
|
str('royalty-bps', flags['royalty-bps']);
|
|
447
448
|
bool('no-mint');
|
|
449
|
+
str('721c', flags['721c'] === 'true' ? 'recommended' : flags['721c']); // bare --721c ≡ recommended
|
|
448
450
|
str('backend', flags.backend);
|
|
449
451
|
str('gateway', flags.gateway);
|
|
450
452
|
str('bucket', flags.bucket);
|
|
@@ -488,6 +490,7 @@ function deploySeriesCommandLine(flags, salt) {
|
|
|
488
490
|
str('minter', flags.minter);
|
|
489
491
|
str('primary-payee', flags['primary-payee']);
|
|
490
492
|
str('royalty-bps', flags['royalty-bps']);
|
|
493
|
+
str('721c', flags['721c'] === 'true' ? 'recommended' : flags['721c']); // bare --721c ≡ recommended
|
|
491
494
|
if (flags.sign !== undefined)
|
|
492
495
|
parts.push('--sign');
|
|
493
496
|
if (flags.unsigned !== undefined)
|
|
@@ -528,6 +531,7 @@ function deployCodeCommandLine(flags, salt) {
|
|
|
528
531
|
str('minter', flags.minter);
|
|
529
532
|
str('primary-payee', flags['primary-payee']);
|
|
530
533
|
str('royalty-bps', flags['royalty-bps']);
|
|
534
|
+
str('721c', flags['721c'] === 'true' ? 'recommended' : flags['721c']); // bare --721c ≡ recommended
|
|
531
535
|
str('backend', flags.backend);
|
|
532
536
|
str('gateway', flags.gateway);
|
|
533
537
|
str('public-base-url', flags['public-base-url']);
|
|
@@ -1210,6 +1214,8 @@ async function cmdDeploy(flags, serveAfter) {
|
|
|
1210
1214
|
// a wrong-network RPC must still be caught with the clear mismatch message rather than failing
|
|
1211
1215
|
// opaquely inside predict; `allowUnreachable` keeps a genuinely offline dry-run previewable.
|
|
1212
1216
|
await assertChainId(CHAIN, { allowUnreachable: dryRun });
|
|
1217
|
+
// --721c (opt-in ERC-721C): absent → zeroAddress, a plain ERC-721 exactly as before.
|
|
1218
|
+
const transferValidator = await resolveTransferValidatorFlag(flags, publicClient, dryRun);
|
|
1213
1219
|
// `demo` is exempt: its whole job is a zero-argument first token.
|
|
1214
1220
|
if (!serveAfter)
|
|
1215
1221
|
assertRealIdentity(flags, { name, symbol, dryRun });
|
|
@@ -1458,6 +1464,7 @@ async function cmdDeploy(flags, serveAfter) {
|
|
|
1458
1464
|
contractURIRenderer: renderer,
|
|
1459
1465
|
royaltyReceiver: deployer,
|
|
1460
1466
|
royaltyBps,
|
|
1467
|
+
transferValidator,
|
|
1461
1468
|
tokenFields,
|
|
1462
1469
|
// authorship + rights (artist / license / …) are collection-scope on-chain fields.
|
|
1463
1470
|
contractFields: authorshipContractFields(flags),
|
|
@@ -1512,7 +1519,8 @@ async function cmdDeploy(flags, serveAfter) {
|
|
|
1512
1519
|
return;
|
|
1513
1520
|
}
|
|
1514
1521
|
// opt-in --confirm: a final y/N before the real send (no-op without --confirm; never blocks scripts)
|
|
1515
|
-
await confirmSend(`About to deploy 1/1 "${name}" (${symbol})${flags.image ? ' with your image' : ' (generative demo art)'}; mint: ${noMint ? 'deferred' : 'token #0 at deploy'}; owner+royalty: your wallet @ ${royaltyBps / 100}
|
|
1522
|
+
await confirmSend(`About to deploy 1/1 "${name}" (${symbol})${flags.image ? ' with your image' : ' (generative demo art)'}; mint: ${noMint ? 'deferred' : 'token #0 at deploy'}; owner+royalty: your wallet @ ${royaltyBps / 100}%.` +
|
|
1523
|
+
(transferValidator !== zeroAddress ? ` ERC-721C: enrolled at deploy, permanently (validator ${transferValidator}).` : ''), flags);
|
|
1516
1524
|
if (lane === 'send') {
|
|
1517
1525
|
// hot lane: the env key is the deployer, known up front.
|
|
1518
1526
|
const { wallet, account } = makeWalletClient({ chainKey: CHAIN });
|
|
@@ -1767,7 +1775,7 @@ async function cmdDeploySeries(flags) {
|
|
|
1767
1775
|
if (!flags.dir) {
|
|
1768
1776
|
throw new Error('abx deploy-series --dir <media-dir> [--count N] [--mint-all | --mint-count N | --no-mint] ' +
|
|
1769
1777
|
'[--onchain-image [--compress fastlz] | --onchain-uri --backend ipfs|arweave|cloud (image off-chain, JSON on-chain; cloud needs --public-base) | --onchain-uri (inline SVG) | --public-base-url https://… (hosted resolver)] ' +
|
|
1770
|
-
'[--minter 0x..] [--primary-payee 0x..] [--unpaused] --name "Title" --symbol SYM');
|
|
1778
|
+
'[--minter 0x..] [--primary-payee 0x..] [--unpaused] [--721c recommended|0x..] --name "Title" --symbol SYM');
|
|
1771
1779
|
}
|
|
1772
1780
|
const dirPath = resolvePath(flags.dir);
|
|
1773
1781
|
const files = readdirSync(dirPath)
|
|
@@ -1790,6 +1798,8 @@ async function cmdDeploySeries(flags) {
|
|
|
1790
1798
|
// Catch a wrong-network RPC with the clear mismatch message even on dry-run (which still reads
|
|
1791
1799
|
// the chain to predict the address); tolerate an unreachable RPC so an offline preview still works.
|
|
1792
1800
|
await assertChainId(CHAIN, { allowUnreachable: dryRun });
|
|
1801
|
+
// --721c (opt-in ERC-721C): absent → zeroAddress, a plain ERC-721 exactly as before.
|
|
1802
|
+
const transferValidator = await resolveTransferValidatorFlag(flags, publicClient, dryRun);
|
|
1793
1803
|
// (identity guard already ran above, via the shared assertRealIdentity — before any RPC)
|
|
1794
1804
|
// Mint timing: mint-all → the whole series; mint-count N → the first N; else deferred.
|
|
1795
1805
|
const mintCount = flags['mint-all'] !== undefined ? count : flags['mint-count'] ? Number(flags['mint-count']) : 0;
|
|
@@ -2004,7 +2014,8 @@ async function cmdDeploySeries(flags) {
|
|
|
2004
2014
|
: onChainUri
|
|
2005
2015
|
? 'inline SVG on-chain'
|
|
2006
2016
|
: `off-chain custody → ${baseUrl}`;
|
|
2007
|
-
await confirmSend(`About to deploy Series "${name}" (${symbol}) — ${count} token(s); ${custody}; mint: ${mintCount > 0 ? `${mintCount} at deploy` : 'deferred'}; owner+royalty: your wallet @ ${royaltyBps / 100}
|
|
2017
|
+
await confirmSend(`About to deploy Series "${name}" (${symbol}) — ${count} token(s); ${custody}; mint: ${mintCount > 0 ? `${mintCount} at deploy` : 'deferred'}; owner+royalty: your wallet @ ${royaltyBps / 100}%.` +
|
|
2018
|
+
(transferValidator !== zeroAddress ? ` ERC-721C: enrolled at deploy, permanently (validator ${transferValidator}).` : ''), flags);
|
|
2008
2019
|
}
|
|
2009
2020
|
if (!onchainImage && !remoteEthUpload)
|
|
2010
2021
|
await buildFields();
|
|
@@ -2023,6 +2034,7 @@ async function cmdDeploySeries(flags) {
|
|
|
2023
2034
|
contractURIRenderer: renderer,
|
|
2024
2035
|
royaltyReceiver: deployer,
|
|
2025
2036
|
royaltyBps,
|
|
2037
|
+
transferValidator,
|
|
2026
2038
|
maxInvocations: count,
|
|
2027
2039
|
primaryPayee,
|
|
2028
2040
|
minter,
|
|
@@ -3092,7 +3104,7 @@ const DEPLOY_CODE_FLAGS = new Set([
|
|
|
3092
3104
|
...AUTHORSHIP_DEPLOY_FIELDS.map(([flag]) => flag), // artist · display-notes · artist-links · license
|
|
3093
3105
|
'image-base', 'attributes-renderer', 'image-renderer',
|
|
3094
3106
|
// supply + mint + economics
|
|
3095
|
-
'max', 'mint-count', 'mint-all', 'no-mint', 'unpaused', 'minter', 'primary-payee', 'royalty-bps',
|
|
3107
|
+
'max', 'mint-count', 'mint-all', 'no-mint', 'unpaused', 'minter', 'primary-payee', 'royalty-bps', '721c',
|
|
3096
3108
|
// params + dependencies
|
|
3097
3109
|
'schema', 'no-seed', 'no-delegation', 'dep', 'dep-registry',
|
|
3098
3110
|
// serving lane
|
|
@@ -3108,6 +3120,7 @@ const DEPLOY_CODE_FLAGS = new Set([
|
|
|
3108
3120
|
const SHARED_DEPLOY_FLAGS = [
|
|
3109
3121
|
'backend', 'endpoint', 'bucket', 'region', 'prefix', 'public-base', 'gateway', 'mode', 'api-url', 'upload-url', 'provider', 'storage-signer',
|
|
3110
3122
|
'sign', 'unsigned', 'for', 'sign-url-file', 'dry-run', 'confirm', 'yes', 'salt', 'factory', 'bootstrap-factory', 'port', 'renderer', 'public-base-url',
|
|
3123
|
+
'721c', // opt-in ERC-721C enrollment (recommended | 0x…) — every deploy path takes it
|
|
3111
3124
|
];
|
|
3112
3125
|
// A 1/1 `abx deploy` accepts these (see `abx help deploy`). Anything else warns (typo'd/unsupported).
|
|
3113
3126
|
const DEPLOY_FLAGS = new Set([
|
|
@@ -3188,7 +3201,7 @@ async function cmdDeployCode(flags) {
|
|
|
3188
3201
|
'[--description "<s>"] [--external-url <url>] [--image-base <url> | --image-renderer 0x..] [--attributes-renderer 0x..] ' +
|
|
3189
3202
|
'[--max N] [--mint-count N | --mint-all] [--schema key:Type:Auth,…] [--no-seed] ' +
|
|
3190
3203
|
'[--dep <name@version|0x..>[,…]] [--dep-registry 0x..] ' +
|
|
3191
|
-
'[--unpaused] [--minter 0x..] [--primary-payee 0x..] [--royalty-bps N] [--backend ipfs|arweave] [--dry-run] [--confirm] [--bootstrap-factory]';
|
|
3204
|
+
'[--unpaused] [--minter 0x..] [--primary-payee 0x..] [--royalty-bps N] [--721c recommended|0x..] [--backend ipfs|arweave] [--dry-run] [--confirm] [--bootstrap-factory]';
|
|
3192
3205
|
const scriptPath = flags.script;
|
|
3193
3206
|
const codeDir = flags['code-dir'];
|
|
3194
3207
|
const hasProgram = !!(scriptPath || codeDir); // a JS program (script or built dir)
|
|
@@ -3261,6 +3274,8 @@ async function cmdDeployCode(flags) {
|
|
|
3261
3274
|
// chain to resolve the factory/renderer and predict the address); tolerate an offline preview.
|
|
3262
3275
|
await assertChainId(CHAIN, { allowUnreachable: dryRun });
|
|
3263
3276
|
const chainId = dryRun ? resolveChain(CHAIN).id : (await publicClient.getChainId());
|
|
3277
|
+
// --721c (opt-in ERC-721C): absent → zeroAddress, a plain ERC-721 exactly as before.
|
|
3278
|
+
const transferValidator = await resolveTransferValidatorFlag(flags, publicClient, dryRun);
|
|
3264
3279
|
step('Trust anchor');
|
|
3265
3280
|
let factory;
|
|
3266
3281
|
if (dryRun) {
|
|
@@ -3666,7 +3681,8 @@ async function cmdDeployCode(flags) {
|
|
|
3666
3681
|
`dependencies: ${deps.length ? deps.map((d) => d.display).join(', ') + (depRegistry ? ` (registry ${depRegistry})` : '') : 'none'}; ` +
|
|
3667
3682
|
`mint: ${mintCount > 0 ? `${mintCount} at deploy${foldIntoInit ? ' (in the deploy tx)' : ''}` : 'deferred'}; ` +
|
|
3668
3683
|
`tx count: ${foldIntoInit ? (schemas.length ? '2 (deploy + enable your params)' : '1 (everything in the deploy tx)') : '2 (deploy + setup)'}; ` +
|
|
3669
|
-
`owner+royalty: ${flags.for ? `pinned to ${String(flags.for)}` : flags.sign !== undefined ? '⚠ the wallet you connect (NOT pinned — pass --for to enforce)' : 'your wallet'} @ ${royaltyBps / 100}
|
|
3684
|
+
`owner+royalty: ${flags.for ? `pinned to ${String(flags.for)}` : flags.sign !== undefined ? '⚠ the wallet you connect (NOT pinned — pass --for to enforce)' : 'your wallet'} @ ${royaltyBps / 100}%.` +
|
|
3685
|
+
(transferValidator !== zeroAddress ? ` ERC-721C: enrolled at deploy, permanently (validator ${transferValidator}).` : '') + `\n` +
|
|
3670
3686
|
` Surfaces — thumbnail: ${imageDisposition}. traits: ${traitsDisposition}. postparams: ${paramsDisposition}.` +
|
|
3671
3687
|
((imageOrphaned || traitsBroken || undeclaredParams.length)
|
|
3672
3688
|
? `\n ⚠ One or more surfaces resolve to NOTHING marketplaces can see — these are DEPLOY-TIME decisions, not backfillable. Re-run --dry-run after fixing, or pass --yes to ship as-is.`
|
|
@@ -3863,6 +3879,7 @@ async function cmdDeployCode(flags) {
|
|
|
3863
3879
|
contractURIRenderer: foldIntoInit ? metadataRenderer : zeroAddress,
|
|
3864
3880
|
royaltyReceiver: owner,
|
|
3865
3881
|
royaltyBps,
|
|
3882
|
+
transferValidator,
|
|
3866
3883
|
maxInvocations: BigInt(max),
|
|
3867
3884
|
primaryPayee: flags['primary-payee'] ?? zeroAddress,
|
|
3868
3885
|
minter: flags.minter ?? zeroAddress,
|
|
@@ -5045,6 +5062,9 @@ const STATE_ABI = [
|
|
|
5045
5062
|
{ type: 'function', name: 'primaryPayee', stateMutability: 'view', inputs: [], outputs: [{ type: 'address' }] },
|
|
5046
5063
|
{ type: 'function', name: 'tokenURIRenderer', stateMutability: 'view', inputs: [], outputs: [{ type: 'address' }] },
|
|
5047
5064
|
{ type: 'function', name: 'royaltyInfo', stateMutability: 'view', inputs: [{ type: 'uint256' }, { type: 'uint256' }], outputs: [{ type: 'address' }, { type: 'uint256' }] },
|
|
5065
|
+
// ERC-721C (creator token) — ERC-165 advertises ICreatorToken ONLY when enrolled at deploy.
|
|
5066
|
+
{ type: 'function', name: 'supportsInterface', stateMutability: 'view', inputs: [{ type: 'bytes4' }], outputs: [{ type: 'bool' }] },
|
|
5067
|
+
{ type: 'function', name: 'getTransferValidator', stateMutability: 'view', inputs: [], outputs: [{ type: 'address' }] },
|
|
5048
5068
|
];
|
|
5049
5069
|
async function cmdState(address, flags) {
|
|
5050
5070
|
if (!address || address.startsWith('--')) {
|
|
@@ -5061,7 +5081,7 @@ async function cmdState(address, flags) {
|
|
|
5061
5081
|
return undefined; // getter absent (extension not composed) or contract has no code
|
|
5062
5082
|
}
|
|
5063
5083
|
};
|
|
5064
|
-
const [owner, totalSupply, nextTokenId, maxInvocations, paused, minter, primaryPayee, renderer, royalty] = await Promise.all([
|
|
5084
|
+
const [owner, totalSupply, nextTokenId, maxInvocations, paused, minter, primaryPayee, renderer, royalty, creatorToken, transferValidator] = await Promise.all([
|
|
5065
5085
|
read('owner'),
|
|
5066
5086
|
read('totalSupply'),
|
|
5067
5087
|
read('nextTokenId'),
|
|
@@ -5071,6 +5091,8 @@ async function cmdState(address, flags) {
|
|
|
5071
5091
|
read('primaryPayee'),
|
|
5072
5092
|
read('tokenURIRenderer'),
|
|
5073
5093
|
read('royaltyInfo', [0n, 10000n]),
|
|
5094
|
+
read('supportsInterface', [CREATOR_TOKEN_INTERFACE_ID]),
|
|
5095
|
+
read('getTransferValidator'),
|
|
5074
5096
|
]);
|
|
5075
5097
|
if (owner === undefined && totalSupply === undefined) {
|
|
5076
5098
|
throw new Error(`no ABX contract state at ${address} on ${CHAIN} (no code, or not an ABX token).`);
|
|
@@ -5095,6 +5117,11 @@ async function cmdState(address, flags) {
|
|
|
5095
5117
|
if (royalty)
|
|
5096
5118
|
info(`royalty ${Number(royalty[1]) / 100}% → ${royalty[0]}`);
|
|
5097
5119
|
info(`renderer ${renderer && renderer !== zeroAddress ? `on-chain (${renderer})` : dim('off-chain (stored URI base / override)')}`);
|
|
5120
|
+
// ERC-721C: shown ONLY for an enrolled collection — an unenrolled token is a plain ERC-721
|
|
5121
|
+
// (indistinguishable from a pre-721C token) and shows nothing new here.
|
|
5122
|
+
if (creatorToken) {
|
|
5123
|
+
info(`721C validator ${transferValidator && transferValidator !== zeroAddress ? transferValidator : `${c.orange}suspended${c.reset} ${dim('(zero — enforcement off; re-enable with `abx set-transfer-validator`)')}`}`);
|
|
5124
|
+
}
|
|
5098
5125
|
// The PostParam surface, read straight from chain: `paramSchemaKeys()` is the DECLARED (governed)
|
|
5099
5126
|
// set — append-only, and it includes keys declared but never yet written, which nothing off-chain
|
|
5100
5127
|
// could otherwise discover — and `contractParamKeys()` is the collection-scope set values. Both
|
|
@@ -6041,6 +6068,10 @@ const COMMAND_HELP = {
|
|
|
6041
6068
|
${g('--compress')} none|fastlz|gzip for --onchain-image (default none): ${g('fastlz')} = on-chain decode (stays renderable);
|
|
6042
6069
|
${g('gzip')} = smaller but off-chain decode only
|
|
6043
6070
|
--royalty-bps <0-10000> default 500 (5%)
|
|
6071
|
+
${g('--721c')} [recommended|0x..] opt-in ERC-721C: enroll at deploy, PERMANENTLY, with that transfer validator
|
|
6072
|
+
(${g('recommended')} = OpenSea's validator for creator-fee enforcement — only owner-initiated
|
|
6073
|
+
transfers + authorized sales move the token). Absent = plain ERC-721, forever (the default).
|
|
6074
|
+
Manage later with ${g('abx set-transfer-validator')} (re-point or suspend; never un-enroll).
|
|
6044
6075
|
${g('--no-mint')} deploy without minting (warm the resolver, then ${g('abx mint')})
|
|
6045
6076
|
--salt 0x..<64hex> deploy to a reserved / vanity address (see ${g('abx predict')})
|
|
6046
6077
|
--public-base-url <url> bake a public resolver URL on-chain (off-chain custody REQUIRES a public URL; localhost is refused)
|
|
@@ -6070,6 +6101,8 @@ const COMMAND_HELP = {
|
|
|
6070
6101
|
a huge collection should set traits post-deploy via ${g('abx set-field')} under a gas budget.
|
|
6071
6102
|
--minter 0x.. authorize a single minting contract --primary-payee 0x.. primary-sale payout
|
|
6072
6103
|
--unpaused open the mint at deploy (default: paused ⇒ owner-only until ${g('abx unpause')})
|
|
6104
|
+
${g('--721c')} [recommended|0x..] opt-in ERC-721C: enroll at deploy, PERMANENTLY, with that transfer validator (absent = plain
|
|
6105
|
+
ERC-721 forever). ${g('recommended')} = OpenSea's creator-fee-enforcement validator. Manage: ${g('abx set-transfer-validator')}
|
|
6073
6106
|
--salt 0x.. · --backend … (storage override) · ${g('--storage-signer')} arweave|eth (who pays Turbo uploads) · signing: ${g('--send')} · ${g('--sign')} · ${g('--unsigned')}
|
|
6074
6107
|
${g('--dry-run')} preview (address · per-token plan · custody · mint plan) WITHOUT sending or storing
|
|
6075
6108
|
${dim('(a dry-run needs a deployer to compute the address — pass --for 0x.. if no signing key is set)')}
|
|
@@ -6151,6 +6184,8 @@ const COMMAND_HELP = {
|
|
|
6151
6184
|
a CDN-served record is the normal production path, on-chain bytes are the durability floor).
|
|
6152
6185
|
--max N supply cap (default 16) mint timing: ${g('--mint-all')} · ${g('--mint-count')} N · (default: defer, then ${g('abx mint')})
|
|
6153
6186
|
--no-seed opt out of the canonical mint-time seed (seeds settle once assigned; curated pre-set seeds win)
|
|
6187
|
+
${g('--721c')} [recommended|0x..] opt-in ERC-721C: enroll at deploy, PERMANENTLY, with that transfer validator (absent = plain
|
|
6188
|
+
ERC-721 forever). ${g('recommended')} = OpenSea's creator-fee-enforcement validator. Manage: ${g('abx set-transfer-validator')}
|
|
6154
6189
|
--backend ipfs|arweave directory-mode custody for the build upload ${g('--unpaused')} · --minter 0x.. · --primary-payee 0x..
|
|
6155
6190
|
${dim('a fixed-price PRIMARY SALE is set up AFTER deploy, not by these flags: ')}${g('abx minter configure <addr> --price <eth>')}${dim(' → ')}${g('set-minter')}${dim(' → ')}${g('set-primary-payee')}${dim(' → ')}${g('unpause')}${dim(' (see `abx minter --help`). --minter/--primary-payee here only pre-authorize an already-known minter.')}
|
|
6156
6191
|
signing: ${g('--send')} (default) · ${g('--sign')} · ${g('--unsigned')} ${dim('(needs --for 0x..)')} ${g('--bootstrap-factory')} ${dim('(private chains/sandboxes only)')}
|
|
@@ -6255,7 +6290,8 @@ const COMMAND_HELP = {
|
|
|
6255
6290
|
signing: ${g('--send')} (default) · ${g('--sign')} · ${g('--unsigned')}`,
|
|
6256
6291
|
state: `
|
|
6257
6292
|
${bold('abx state')} <address> ${dim('— read-only, on-chain operational snapshot (no tx, no local index).')}
|
|
6258
|
-
Shows owner · supply (minted / max, nextTokenId) · paused · minter · primary payee · royalty · renderer
|
|
6293
|
+
Shows owner · supply (minted / max, nextTokenId) · paused · minter · primary payee · royalty · renderer
|
|
6294
|
+
(+ the 721C transfer validator, for a collection that enrolled at deploy — plain ERC-721s show nothing new).
|
|
6259
6295
|
Series-only fields are shown for a Series; a 1/1 shows just supply + royalty + renderer.
|
|
6260
6296
|
Handy before/after owner ops (mint · pause/unpause · set-minter · set-primary-payee).
|
|
6261
6297
|
${dim('state = what the CHAIN says. For who is SERVING it and how fresh that is, see `abx status`.')}`,
|
|
@@ -6310,6 +6346,14 @@ const COMMAND_HELP = {
|
|
|
6310
6346
|
deploy to Vercel). ${dim('Offer this after a sale is live (configure → set-minter → set-primary-payee → unpause).')}`,
|
|
6311
6347
|
'set-royalty': `
|
|
6312
6348
|
${bold('abx set-royalty')} <address> --bps <0-10000> [--receiver 0x..] ${dim('— change the royalty. Sends a tx.')}`,
|
|
6349
|
+
'set-transfer-validator': `
|
|
6350
|
+
${bold('abx set-transfer-validator')} <address> <0x..|none|recommended> ${dim('— manage an ERC-721C collection\'s transfer validator. Owner-only. Sends a tx.')}
|
|
6351
|
+
<0x..> re-point enforcement at that validator (must be a DEPLOYED contract — checked before any gas)
|
|
6352
|
+
${g('none')} suspend enforcement (validator → address(0); the collection STAYS enrolled)
|
|
6353
|
+
${g('recommended')} the chain's recommended validator (OpenSea's, for creator-fee enforcement)
|
|
6354
|
+
${dim('Only for a collection that enrolled AT DEPLOY (--721c on the deploy commands) — a plain ERC-721 is refused')}
|
|
6355
|
+
${dim('up front: enrollment is a deploy-time decision and can never be added (or fully removed) later.')}
|
|
6356
|
+
signing: ${g('--send')} (default) · ${g('--sign')} · ${g('--unsigned')} · ${g('--dry-run')} preview (encodes + shows the tx, sends nothing)`,
|
|
6313
6357
|
attach: `
|
|
6314
6358
|
${bold('abx attach')} <address> <key> <ipfs://… | ar://… | https://…> ${dim('— attach a named file to a token. Sends a tx.')}
|
|
6315
6359
|
${dim('The data-plane verb: the file joins the token\'s served')} ${bold('artifacts')} ${dim('manifest as {key, mimeType, uri}.')}
|
|
@@ -6543,6 +6587,7 @@ function help() {
|
|
|
6543
6587
|
${g('abx set-contract-uri')} <addr> re-point the collection URI --uri <url>
|
|
6544
6588
|
${g('abx migrate')} <addr> move off-chain state to a NEW resolver (verifies parity; no cutover) --from <old> --to <new>
|
|
6545
6589
|
${g('abx set-royalty')} <addr> change royalty --bps <0-10000> [--receiver 0x..]
|
|
6590
|
+
${g('abx set-transfer-validator')} <addr> <0x..|none|recommended> manage an ERC-721C collection's validator (enrolled-at-deploy only)
|
|
6546
6591
|
${g('abx attach')} <addr> <key> <ipfs://…|ar://…|https://…> attach a named file → the token's ${bold('artifacts')} manifest (data plane)
|
|
6547
6592
|
${g('abx set-field')} <addr> set an on-chain metadata field (low-level) --field <name> (--text "…" | --value 0x..) [--collection]
|
|
6548
6593
|
${g('abx lock-field')} <addr> freeze a field forever --field <name> [--collection]
|
|
@@ -6576,6 +6621,48 @@ function help() {
|
|
|
6576
6621
|
${dim('abx checks npm for a newer release (every 6h, notify-only). Silence with')} ${g('ABX_NO_UPDATE_CHECK=1')} ${dim('or')} ${g('--no-update-check')}${dim('.')}
|
|
6577
6622
|
`);
|
|
6578
6623
|
}
|
|
6624
|
+
/**
|
|
6625
|
+
* Resolve `--721c` into the InitParams `transferValidator`. Absent → `zeroAddress`: a plain
|
|
6626
|
+
* ERC-721, byte-for-byte the pre-721C deploy — no prompts, no output, no mention of 721C.
|
|
6627
|
+
* `--721c` / `--721c recommended` → the per-chain recommended validator (refused, naming the
|
|
6628
|
+
* chains that have one, when the manifest has no entry); `--721c 0x…` → checksum-validated AND
|
|
6629
|
+
* pre-checked for code on this chain (the factory would revert `InvalidTransferValidator()` —
|
|
6630
|
+
* surface it before any upload/staging/gas). When enrolling, prints the one plain statement of
|
|
6631
|
+
* what enforcement means — once, adapted to the chosen validator.
|
|
6632
|
+
*/
|
|
6633
|
+
async function resolveTransferValidatorFlag(flags, publicClient, dryRun) {
|
|
6634
|
+
const raw = flags['721c'];
|
|
6635
|
+
if (raw === undefined)
|
|
6636
|
+
return zeroAddress;
|
|
6637
|
+
const chainId = resolveChain(CHAIN).id;
|
|
6638
|
+
const validator = parseTransferValidatorValue(raw, { chainId, chainLabel: CHAIN });
|
|
6639
|
+
// Has-code precheck — for the recommended constant too (presence on THIS chain is the fact that
|
|
6640
|
+
// matters; a sandbox/private chain won't have it). A dry run tolerates an unreachable RPC (it
|
|
6641
|
+
// sends nothing); a real deploy refuses to enroll blind.
|
|
6642
|
+
let code;
|
|
6643
|
+
try {
|
|
6644
|
+
code = await publicClient.getCode({ address: validator });
|
|
6645
|
+
}
|
|
6646
|
+
catch (err) {
|
|
6647
|
+
if (!dryRun)
|
|
6648
|
+
throw new Error(`--721c: couldn't verify the validator has code at ${validator} (${err.message}) — refusing to enroll blind; retry when the RPC answers.`);
|
|
6649
|
+
code = null; // offline preview — note it below
|
|
6650
|
+
}
|
|
6651
|
+
if (code === null) {
|
|
6652
|
+
info(dim(`--721c: RPC unreachable, so the validator code check is deferred — a real deploy verifies ${validator} has code first.`));
|
|
6653
|
+
}
|
|
6654
|
+
else if (!code || code === '0x') {
|
|
6655
|
+
const rec = resolveRecommendedTransferValidator(chainId);
|
|
6656
|
+
throw new Error(`--721c: no contract code at ${validator} on ${CHAIN} — the deploy would revert InvalidTransferValidator(). ` +
|
|
6657
|
+
`A transfer validator must be a DEPLOYED contract on this chain` +
|
|
6658
|
+
(rec && rec !== validator ? ` (the known-good one: --721c recommended → ${rec})` : '') + `.`);
|
|
6659
|
+
}
|
|
6660
|
+
// The one plain statement — printed once, only when enrolling. Never appears un-enrolled.
|
|
6661
|
+
info(validator === resolveRecommendedTransferValidator(chainId)
|
|
6662
|
+
? `ERC-721C enforcement: only owner-initiated transfers and OpenSea-authorized sales will transfer; other marketplaces/operators are blocked. Manage with \`abx set-transfer-validator\`.`
|
|
6663
|
+
: `ERC-721C enforcement: every non-mint transfer is checked by validator ${validator} — its policy decides which operators may transfer. Manage with \`abx set-transfer-validator\`.`);
|
|
6664
|
+
return validator;
|
|
6665
|
+
}
|
|
6579
6666
|
/** Validate an explicit --salt (a 32-byte hex). Undefined when absent. */
|
|
6580
6667
|
function parseSaltFlag(raw) {
|
|
6581
6668
|
if (!raw || raw === 'true')
|