@artblocks/abx-cli 0.1.0-alpha.14 → 0.1.0-alpha.16
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 +1243 -0
- package/assets/renderer-scaffold/src/interfaces/IAbxParams.sol +17 -0
- package/assets/renderer-scaffold/test/MyRenderer.t.sol +51 -2
- package/dist/flags.d.ts +7 -0
- package/dist/flags.d.ts.map +1 -1
- package/dist/flags.js +19 -0
- package/dist/flags.js.map +1 -1
- package/dist/main.js +267 -35
- package/dist/main.js.map +1 -1
- package/dist/ownerops.d.ts +53 -1
- package/dist/ownerops.d.ts.map +1 -1
- package/dist/ownerops.js +181 -13
- package/dist/ownerops.js.map +1 -1
- package/dist/scaffold.d.ts +10 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +52 -0
- package/dist/scaffold.js.map +1 -0
- package/package.json +7 -6
- package/skill/SKILL.md +6 -2
- package/skill/reference/code-projects.md +4 -3
- package/skill/reference/creator-token.md +71 -0
- package/skill/reference/operating.md +2 -0
package/dist/ownerops.d.ts
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 { planContentTxs, type OnChainParamSchema, type SendStagingTx, type Address, type OnChainFieldInput } from '@artblocks/abx-sdk';
|
|
9
|
+
import { planContentTxs, type OnChainParamSchema, type SendStagingTx, type Address, type Hex, type OnChainFieldInput } from '@artblocks/abx-sdk';
|
|
10
10
|
import { type Lane, type WalletSession } from './signer.js';
|
|
11
11
|
import { type ParsedSchema } from './schema.js';
|
|
12
12
|
type Flags = Record<string, string | undefined>;
|
|
@@ -20,6 +20,33 @@ export declare function laneFromFlags(flags: Flags): Lane;
|
|
|
20
20
|
/** Throw a clear, actionable error when `address` has no contract on the active chain — the common
|
|
21
21
|
* cause of an owner-op's raw `returned no data ("0x")`. No-op when code is present or unknowable. */
|
|
22
22
|
export declare function assertContractExists(address: Address): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* `abx configure-param <address> <tokenId> <key> <value>` — set a schema-governed
|
|
25
|
+
* PostParam through the typed path, any lane. Reads the key's on-chain schema first
|
|
26
|
+
* and canonically ENCODES the human input per its type (`#rrggbb`, decimals ×1e10,
|
|
27
|
+
* Select by label, …); `String`/`Bytes` schemas take the value as UTF-8 (or
|
|
28
|
+
* `--file <path>` for bytes) via the data path. The signer must satisfy the schema's
|
|
29
|
+
* auth (Artist = contract owner, TokenOwner — delegate.xyz honored — or the named
|
|
30
|
+
* address); the chain enforces it either way.
|
|
31
|
+
*/
|
|
32
|
+
/** Positional args only — drops `--flags` AND the single token each value-taking flag consumes
|
|
33
|
+
* (mirrors parseFlags). `rest.filter(r => !r.startsWith('--'))` was NOT enough: a flag's VALUE
|
|
34
|
+
* (e.g. the URL after `--remote`, or the path after `--file`) isn't `--`-prefixed, so it leaked
|
|
35
|
+
* into the positional value (`configure-param … #ff0000 --remote http://h` → value "#ff0000 http://h"). */
|
|
36
|
+
/**
|
|
37
|
+
* Encode a command-line value for a payload-typed param (`String` / `Bytes`).
|
|
38
|
+
*
|
|
39
|
+
* `String` is UTF-8 text — the literal characters are the value, which is what a user means.
|
|
40
|
+
*
|
|
41
|
+
* `Bytes` is NOT text, and the old code UTF-8'd whatever string it was handed. A tester passed
|
|
42
|
+
* base64 (the encoding the params docs mention — which describes the *canonical decode* a program
|
|
43
|
+
* receives, not what you type here), and 128 packed bytes were stored as 172 bytes of base64 ASCII.
|
|
44
|
+
* Nothing errored; the in-chain renderer read ASCII where it expected bytes and drew garbage. So a
|
|
45
|
+
* `Bytes` value must state its encoding: `0x…` hex, or `--file` for real binary. A bare string is
|
|
46
|
+
* refused rather than guessed at — there is no safe guess between "these characters" and "these
|
|
47
|
+
* bytes", and the failure is invisible until an artwork renders wrong.
|
|
48
|
+
*/
|
|
49
|
+
export declare function encodePayloadParam(typeName: 'String' | 'Bytes', valueInput: string, key: string): Hex;
|
|
23
50
|
export declare function cmdConfigureParam(address: string | undefined, rest: string[], flags: Flags): Promise<void>;
|
|
24
51
|
declare const HOOK_ROLES: readonly ["configure", "augment", "transfer"];
|
|
25
52
|
type HookRole = (typeof HOOK_ROLES)[number];
|
|
@@ -80,6 +107,31 @@ export declare function cmdSetContractUri(address: string | undefined, flags: Fl
|
|
|
80
107
|
* ABI read). Exported for the regression test. */
|
|
81
108
|
export declare function parseRoyaltyBps(raw: string): number;
|
|
82
109
|
export declare function cmdSetRoyalty(address: string | undefined, flags: Flags): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Parse a transfer-validator choice — the shared grammar of the deploy flag (`--721c`) and the
|
|
112
|
+
* owner op (`abx set-transfer-validator`):
|
|
113
|
+
* - `recommended` (or a bare `--721c`) → the per-chain recommended validator, refusing on a
|
|
114
|
+
* chain the manifest has no entry for (naming the chains that do — never guess one);
|
|
115
|
+
* - a `0x…` address → checksum-validated (EIP-55) and returned as its canonical form;
|
|
116
|
+
* - `none`/zero → `allowNone` decides: the owner op suspends with it; the DEPLOY flag refuses
|
|
117
|
+
* it (a zero validator at deploy never enrolls — that is already the default, so someone
|
|
118
|
+
* passing it either wants plain ERC-721 (drop the flag) or mistakenly believes "enrolled but
|
|
119
|
+
* suspended" is a deploy-time state — it isn't).
|
|
120
|
+
* Pure (no RPC) — callers do the has-code precheck themselves. Exported for the regression test.
|
|
121
|
+
*/
|
|
122
|
+
export declare function parseTransferValidatorValue(raw: string, opts: {
|
|
123
|
+
chainId: number;
|
|
124
|
+
chainLabel: string;
|
|
125
|
+
allowNone?: boolean;
|
|
126
|
+
}): Address;
|
|
127
|
+
/**
|
|
128
|
+
* `abx set-transfer-validator <address> <0x…|none|recommended>` — re-point an ENROLLED (ERC-721C)
|
|
129
|
+
* collection's transfer validator, or suspend enforcement with `none` (address(0); the token
|
|
130
|
+
* STAYS enrolled). Refuses up front, before any signing: a plain ERC-721 (enrollment is
|
|
131
|
+
* deploy-time-only — the contract would revert `NotCreatorToken()`), and a codeless validator
|
|
132
|
+
* (would revert `InvalidTransferValidator()`). Owner-only, any lane, guards `--dry-run`.
|
|
133
|
+
*/
|
|
134
|
+
export declare function cmdSetTransferValidator(address: string | undefined, rest: string[], flags: Flags): Promise<void>;
|
|
83
135
|
export type Compress = 'none' | 'fastlz' | 'gzip';
|
|
84
136
|
export declare function parseCompress(v: string | undefined): Compress;
|
|
85
137
|
/**
|
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, prepareTransfer, prepareTransferOwnership, 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, 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';
|
|
@@ -20,7 +20,7 @@ import { formatEther, getAddress, isAddress, parseEther, toHex, zeroAddress } fr
|
|
|
20
20
|
import { fixedPriceMinterAddress } from './config.js';
|
|
21
21
|
import { openWalletSession, signTx } from './signer.js';
|
|
22
22
|
import { resolveRemote, serviceClient } from './remote.js';
|
|
23
|
-
import { unknownFlags } from './flags.js';
|
|
23
|
+
import { positionalArgs, unknownFlags } from './flags.js';
|
|
24
24
|
import { parseSchemaSpecs, describeSchema } from './schema.js';
|
|
25
25
|
const CHAIN = process.env.ABX_CHAIN ?? DEFAULT_CHAIN_KEY; // base-sepolia default — MUST match main.ts/config.ts (a stale 'sepolia' here silently ran every owner-op on the wrong chain)
|
|
26
26
|
// ── ANSI (local) ─────────────────────────────────────────────────────────────
|
|
@@ -220,18 +220,35 @@ export async function assertContractExists(address) {
|
|
|
220
220
|
* (mirrors parseFlags). `rest.filter(r => !r.startsWith('--'))` was NOT enough: a flag's VALUE
|
|
221
221
|
* (e.g. the URL after `--remote`, or the path after `--file`) isn't `--`-prefixed, so it leaked
|
|
222
222
|
* into the positional value (`configure-param … #ff0000 --remote http://h` → value "#ff0000 http://h"). */
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Encode a command-line value for a payload-typed param (`String` / `Bytes`).
|
|
225
|
+
*
|
|
226
|
+
* `String` is UTF-8 text — the literal characters are the value, which is what a user means.
|
|
227
|
+
*
|
|
228
|
+
* `Bytes` is NOT text, and the old code UTF-8'd whatever string it was handed. A tester passed
|
|
229
|
+
* base64 (the encoding the params docs mention — which describes the *canonical decode* a program
|
|
230
|
+
* receives, not what you type here), and 128 packed bytes were stored as 172 bytes of base64 ASCII.
|
|
231
|
+
* Nothing errored; the in-chain renderer read ASCII where it expected bytes and drew garbage. So a
|
|
232
|
+
* `Bytes` value must state its encoding: `0x…` hex, or `--file` for real binary. A bare string is
|
|
233
|
+
* refused rather than guessed at — there is no safe guess between "these characters" and "these
|
|
234
|
+
* bytes", and the failure is invisible until an artwork renders wrong.
|
|
235
|
+
*/
|
|
236
|
+
export function encodePayloadParam(typeName, valueInput, key) {
|
|
237
|
+
if (typeName === 'String')
|
|
238
|
+
return toHexSdk(new TextEncoder().encode(valueInput));
|
|
239
|
+
const v = valueInput.trim();
|
|
240
|
+
if (/^0x[0-9a-fA-F]*$/.test(v)) {
|
|
241
|
+
if (v.length % 2 !== 0) {
|
|
242
|
+
throw new Error(`--${key} hex value has an odd number of digits (${v.length - 2}) — a byte is two hex digits.`);
|
|
231
243
|
}
|
|
232
|
-
|
|
244
|
+
return v;
|
|
233
245
|
}
|
|
234
|
-
|
|
246
|
+
throw new Error(`"${key}" is a Bytes param, so its value must say what its bytes ARE — this looks like text.\n` +
|
|
247
|
+
` Pass 0x-prefixed hex: abx configure-param <addr> <id> ${key} 0x00112233…\n` +
|
|
248
|
+
` Or the bytes in a file: abx configure-param <addr> <id> ${key} --file ./payload.bin\n` +
|
|
249
|
+
` (Base64 is how a Bytes param is DECODED for your program — not how you write it here. ` +
|
|
250
|
+
`Storing base64 text would put ASCII on-chain where a renderer expects bytes, silently. ` +
|
|
251
|
+
`To store these literal characters on purpose, declare the key as String instead.)`);
|
|
235
252
|
}
|
|
236
253
|
export async function cmdConfigureParam(address, rest, flags) {
|
|
237
254
|
const usage = 'abx configure-param <address> <tokenId|-> <key> <value> [--file <path>] [--remote [url]] [--sign|--unsigned] (tokenId "-" = contract scope, schema-less keys only)';
|
|
@@ -289,7 +306,12 @@ export async function cmdConfigureParam(address, rest, flags) {
|
|
|
289
306
|
if (typeName === 'String' || typeName === 'Bytes') {
|
|
290
307
|
const data = flags.file
|
|
291
308
|
? toHexSdk(new Uint8Array(readFileSync(flags.file)))
|
|
292
|
-
:
|
|
309
|
+
: encodePayloadParam(typeName, valueInput, key);
|
|
310
|
+
const bytes = (data.length - 2) / 2;
|
|
311
|
+
// Echo the DECODED byte count. The old code echoed the string's length, which is precisely how a
|
|
312
|
+
// wrong encoding announced itself and was missed: 128 packed bytes passed as base64 printed
|
|
313
|
+
// "172 bytes".
|
|
314
|
+
console.log(` ${key} (${typeName}) ← ${bytes} bytes ${dim(flags.file ? '(file contents, verbatim)' : typeName === 'Bytes' ? '(decoded from hex)' : '(UTF-8 text)')}`);
|
|
293
315
|
sent = await runWrite(contract, prepareConfigureTokenParamData({ contract, tokenId, key, data, chainId: chainId() }), flags);
|
|
294
316
|
}
|
|
295
317
|
else {
|
|
@@ -661,6 +683,112 @@ export async function cmdSetRoyalty(address, flags) {
|
|
|
661
683
|
}
|
|
662
684
|
await runWrite(contract, prepareSetRoyalty({ contract, receiver, bps, chainId: chainId() }), flags, owner);
|
|
663
685
|
}
|
|
686
|
+
// ── ERC-721C (creator token) — the transfer-validator surface ─────────────────
|
|
687
|
+
// Enrollment is a DEPLOY-TIME decision (`--721c` on the deploy commands) and permanent in both
|
|
688
|
+
// directions: an unenrolled token can never gain a validator, an enrolled one never sheds the
|
|
689
|
+
// standard. Within an enrolled token the owner re-points or suspends (zero) the validator freely.
|
|
690
|
+
/** The chain keys the manifest recommends a transfer validator for — for refusal messages. */
|
|
691
|
+
function chainsWithRecommendedValidator() {
|
|
692
|
+
const ids = new Set(Object.keys(RECOMMENDED_TRANSFER_VALIDATOR).map(Number));
|
|
693
|
+
return KNOWN_CHAIN_KEYS.filter((k) => ids.has(resolveChain(k).id)).join(', ');
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Parse a transfer-validator choice — the shared grammar of the deploy flag (`--721c`) and the
|
|
697
|
+
* owner op (`abx set-transfer-validator`):
|
|
698
|
+
* - `recommended` (or a bare `--721c`) → the per-chain recommended validator, refusing on a
|
|
699
|
+
* chain the manifest has no entry for (naming the chains that do — never guess one);
|
|
700
|
+
* - a `0x…` address → checksum-validated (EIP-55) and returned as its canonical form;
|
|
701
|
+
* - `none`/zero → `allowNone` decides: the owner op suspends with it; the DEPLOY flag refuses
|
|
702
|
+
* it (a zero validator at deploy never enrolls — that is already the default, so someone
|
|
703
|
+
* passing it either wants plain ERC-721 (drop the flag) or mistakenly believes "enrolled but
|
|
704
|
+
* suspended" is a deploy-time state — it isn't).
|
|
705
|
+
* Pure (no RPC) — callers do the has-code precheck themselves. Exported for the regression test.
|
|
706
|
+
*/
|
|
707
|
+
export function parseTransferValidatorValue(raw, opts) {
|
|
708
|
+
const s = raw.trim().toLowerCase();
|
|
709
|
+
if (s === 'true' || s === '' || s === 'recommended') {
|
|
710
|
+
const rec = resolveRecommendedTransferValidator(opts.chainId);
|
|
711
|
+
if (!rec) {
|
|
712
|
+
throw new Error(`no recommended transfer validator is known for '${opts.chainLabel}' (chainId ${opts.chainId}) — ` +
|
|
713
|
+
`chains with one: ${chainsWithRecommendedValidator() || '(none shipped)'}. ` +
|
|
714
|
+
`Pass an explicit validator address instead (it must be a deployed contract on this chain).`);
|
|
715
|
+
}
|
|
716
|
+
return rec;
|
|
717
|
+
}
|
|
718
|
+
if (s === 'none' || s === 'zero' || s === '0' || s === '0x0' || s === zeroAddress) {
|
|
719
|
+
if (opts.allowNone)
|
|
720
|
+
return zeroAddress;
|
|
721
|
+
throw new Error(`a zero transfer validator never enrolls — plain ERC-721 is already the default, so drop --721c. ` +
|
|
722
|
+
`("enrolled but suspended" is not a deploy-time state: enroll with a real validator, then suspend ` +
|
|
723
|
+
`with \`abx set-transfer-validator <address> none\`.)`);
|
|
724
|
+
}
|
|
725
|
+
const trimmed = raw.trim();
|
|
726
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(trimmed)) {
|
|
727
|
+
throw new Error(`transfer validator must be 'recommended'${opts.allowNone ? ", 'none'," : ''} or a 0x address (0x + 40 hex); got '${raw}'`);
|
|
728
|
+
}
|
|
729
|
+
// `isAddress` strict-validates the EIP-55 checksum of a mixed-case address (all-lowercase carries
|
|
730
|
+
// no checksum and passes); `getAddress` alone only NORMALIZES — it would silently accept a
|
|
731
|
+
// mis-cased paste, which is exactly the transposition this check exists to catch.
|
|
732
|
+
if (!isAddress(trimmed, { strict: true })) {
|
|
733
|
+
throw new Error(`transfer validator address failed its EIP-55 checksum: '${raw}' — paste it exactly (or all-lowercase).`);
|
|
734
|
+
}
|
|
735
|
+
return getAddress(trimmed);
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* `abx set-transfer-validator <address> <0x…|none|recommended>` — re-point an ENROLLED (ERC-721C)
|
|
739
|
+
* collection's transfer validator, or suspend enforcement with `none` (address(0); the token
|
|
740
|
+
* STAYS enrolled). Refuses up front, before any signing: a plain ERC-721 (enrollment is
|
|
741
|
+
* deploy-time-only — the contract would revert `NotCreatorToken()`), and a codeless validator
|
|
742
|
+
* (would revert `InvalidTransferValidator()`). Owner-only, any lane, guards `--dry-run`.
|
|
743
|
+
*/
|
|
744
|
+
export async function cmdSetTransferValidator(address, rest, flags) {
|
|
745
|
+
const usage = 'abx set-transfer-validator <address> <0x…|none|recommended> [--sign|--unsigned] [--dry-run]';
|
|
746
|
+
const contract = requireAddress(address, usage);
|
|
747
|
+
const [raw] = positionalArgs(rest);
|
|
748
|
+
if (!raw) {
|
|
749
|
+
console.error(`usage: ${usage}\n`);
|
|
750
|
+
process.exit(1);
|
|
751
|
+
}
|
|
752
|
+
const cid = chainId();
|
|
753
|
+
const validator = parseTransferValidatorValue(raw, { chainId: cid, chainLabel: CHAIN, allowNone: true });
|
|
754
|
+
const publicClient = makePublicClient({ chainKey: CHAIN });
|
|
755
|
+
// Enrollment guard FIRST — a read, not a send. An unenrolled token would revert
|
|
756
|
+
// `NotCreatorToken()`; refuse with the real story instead of letting the chain say it in hex.
|
|
757
|
+
// (readCreatorTokenStatus is defensive, so check the contract exists first — a typo'd address
|
|
758
|
+
// must not read as "plain ERC-721".)
|
|
759
|
+
await assertContractExists(contract);
|
|
760
|
+
const status = await readCreatorTokenStatus(publicClient, contract);
|
|
761
|
+
if (!status.enrolled) {
|
|
762
|
+
throw new Error(`${contract} is a plain ERC-721 — 721C is a deploy-time decision, and this collection didn't enroll. ` +
|
|
763
|
+
`Enrollment can never be added to a live collection; if enforcement is required, redeploy with ` +
|
|
764
|
+
`--721c recommended (or --721c 0x…) on the deploy command.`);
|
|
765
|
+
}
|
|
766
|
+
// Codeless-validator guard: the contract refuses a non-zero validator with no code
|
|
767
|
+
// (`InvalidTransferValidator()`) — surface it before gas is spent.
|
|
768
|
+
if (validator !== zeroAddress) {
|
|
769
|
+
let code;
|
|
770
|
+
try {
|
|
771
|
+
code = await publicClient.getCode({ address: validator });
|
|
772
|
+
}
|
|
773
|
+
catch (err) {
|
|
774
|
+
throw new Error(`couldn't verify the validator has code at ${validator} (${err.message}) — refusing to re-point blind; retry when the RPC answers.`);
|
|
775
|
+
}
|
|
776
|
+
if (!code || code === '0x') {
|
|
777
|
+
const rec = resolveRecommendedTransferValidator(cid);
|
|
778
|
+
throw new Error(`no contract code at ${validator} on ${CHAIN} — the token would revert InvalidTransferValidator(). ` +
|
|
779
|
+
`A transfer validator must be a DEPLOYED contract on this chain` +
|
|
780
|
+
(rec ? ` (the recommended one: \`abx set-transfer-validator ${contract} recommended\` → ${rec})` : '') + `.`);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
if (eqAddr(status.validator, validator)) {
|
|
784
|
+
console.log(dim(` no change — the validator is already ${validator === zeroAddress ? 'suspended (0x0)' : validator}. Nothing sent.`));
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
const label = (a) => (eqAddr(a, zeroAddress) ? 'suspended (0x0)' : a);
|
|
788
|
+
console.log(dim(` transfer validator: ${label(status.validator)} → ${label(validator)}${validator === zeroAddress ? ' — transfers go unvalidated until one is set again (the token stays enrolled)' : ''}`));
|
|
789
|
+
const owner = await read(contract, 'owner');
|
|
790
|
+
await runWrite(contract, prepareSetTransferValidator({ contract, validator, chainId: cid }), flags, owner);
|
|
791
|
+
}
|
|
664
792
|
export function parseCompress(v) {
|
|
665
793
|
const c = (v ?? 'none').toLowerCase();
|
|
666
794
|
if (c === 'none' || c === 'fastlz' || c === 'gzip')
|
|
@@ -1074,10 +1202,50 @@ export async function cmdAttach(rest, flags) {
|
|
|
1074
1202
|
}
|
|
1075
1203
|
}
|
|
1076
1204
|
// ── lock-field ───────────────────────────────────────────────────────────────
|
|
1205
|
+
/**
|
|
1206
|
+
* Refuse `lock-field` on a name that is a declared PARAM key.
|
|
1207
|
+
*
|
|
1208
|
+
* Fields and params are two separate namespaces that may share a name, and `lock-field` only ever
|
|
1209
|
+
* locks the *field*. A tester welded `grid` — a `Bytes` param holding the artwork — with
|
|
1210
|
+
* `lock-field --field grid`, got "permanent", got `tokenFieldLocked(0,"grid") == true`, and then
|
|
1211
|
+
* overwrote the artwork with `configure-param` on the next call. Every individual statement the CLI
|
|
1212
|
+
* made was true; together they promised a protection that did not exist. Permanence is the pitch, so
|
|
1213
|
+
* this refuses rather than warns, and names the mechanism that actually welds a param.
|
|
1214
|
+
*/
|
|
1215
|
+
async function refuseIfParamKey(contract, field) {
|
|
1216
|
+
let exists = false;
|
|
1217
|
+
try {
|
|
1218
|
+
const schema = (await makePublicClient({ chainKey: CHAIN }).readContract({
|
|
1219
|
+
address: contract,
|
|
1220
|
+
abi: seriesCodeAbi,
|
|
1221
|
+
functionName: 'paramSchema',
|
|
1222
|
+
args: [encodeTagSdk(field)],
|
|
1223
|
+
}));
|
|
1224
|
+
exists = !!schema[0];
|
|
1225
|
+
}
|
|
1226
|
+
catch {
|
|
1227
|
+
// No param surface at all (a 1/1 or plain Series) — nothing to confuse the name with.
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
if (!exists)
|
|
1231
|
+
return;
|
|
1232
|
+
throw new Error(`"${field}" is a declared PostParam key on ${contract}, and lock-field does NOT lock params — ` +
|
|
1233
|
+
`it locks the metadata FIELD of the same name. They are separate namespaces, so this would have ` +
|
|
1234
|
+
`reported "permanent" while configure-param stayed free to overwrite the value.\n` +
|
|
1235
|
+
` To weld the param, lock its schema instead:\n` +
|
|
1236
|
+
` abx set-schema ${contract} --schema ${field}:<Type>:<Auth>:lock=now\n` +
|
|
1237
|
+
` (after that every configure-param on "${field}" reverts ParamLockExpired — check with ` +
|
|
1238
|
+
`\`abx inspect ${contract}\`.)\n` +
|
|
1239
|
+
` If you really did mean the metadata field "${field}" and not the param, re-run with --force-field.`);
|
|
1240
|
+
}
|
|
1077
1241
|
export async function cmdLockField(address, flags) {
|
|
1078
1242
|
const contract = requireAddress(address, 'abx lock-field <address> --field <name> [--collection | --token 0] [--sign|--unsigned]');
|
|
1079
1243
|
const field = requireFlag(flags, 'field', 'abx lock-field <address> --field <name>');
|
|
1080
1244
|
const collection = !!flags.collection;
|
|
1245
|
+
// The metadata field and a same-named param are different things; only an explicit --force-field
|
|
1246
|
+
// says "yes, I mean the field". See refuseIfParamKey.
|
|
1247
|
+
if (flags['force-field'] === undefined)
|
|
1248
|
+
await refuseIfParamKey(contract, field);
|
|
1081
1249
|
const owner = await read(contract, 'owner');
|
|
1082
1250
|
console.log(dim(` note: locking the '${field}' field is permanent and irreversible (freezes all its representations).`));
|
|
1083
1251
|
const tx = collection
|