@artblocks/abx-cli 0.1.0-alpha.26 → 0.1.0-alpha.28
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 +179 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +118 -19
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/project.d.ts +32 -0
- package/dist/commands/project.d.ts.map +1 -1
- package/dist/commands/project.js +59 -2
- package/dist/commands/project.js.map +1 -1
- package/dist/commands/reads.js +1 -1
- package/dist/commands/reads.js.map +1 -1
- package/dist/commands/scaffold.d.ts.map +1 -1
- package/dist/commands/scaffold.js +17 -6
- package/dist/commands/scaffold.js.map +1 -1
- package/dist/flag-allowlists.d.ts.map +1 -1
- package/dist/flag-allowlists.js +1 -0
- package/dist/flag-allowlists.js.map +1 -1
- package/dist/main.js +19 -8
- package/dist/main.js.map +1 -1
- package/dist/ownerops.d.ts +13 -1
- package/dist/ownerops.d.ts.map +1 -1
- package/dist/ownerops.js +40 -6
- package/dist/ownerops.js.map +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +10 -1
- package/dist/schema.js.map +1 -1
- package/package.json +6 -6
- package/skill/SKILL.md +272 -172
- package/skill/reference/decisions.md +44 -3
- package/skill/reference/operating.md +36 -2
- package/skill/reference/setup.md +42 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,184 @@
|
|
|
1
1
|
# @artblocks/abx-cli
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.28
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 84ca9dd: Off-chain fixes from the 2026-08-19 production-readiness audit
|
|
8
|
+
(`reviews/2026-08/production-readiness-audit.md`). All are off-chain — no contract changed and no
|
|
9
|
+
address moved; the three NatSpec-precision items the audit also raised ride the next redeploy batch
|
|
10
|
+
instead (`docs/10-backlog.md` → _Contracts — the next redeploy batch_, B47).
|
|
11
|
+
|
|
12
|
+
**SDK — `HexColor` decode now masks to 24 bits, matching the chain (L-1).** `decodeScalarParam` for a
|
|
13
|
+
`HexColor` value greater than `0xFFFFFF` returned `#1ffffff` (7+ hex digits); the on-chain
|
|
14
|
+
`TokenDataLib.decodeScalar` masks with `& 0xffffff` and returns `#ffffff`. The governed write validates
|
|
15
|
+
`v <= 0xFFFFFF`, but the raw owner setter does not, so an out-of-domain value can be stored and later
|
|
16
|
+
governed by a `HexColor` schema — and the two serving planes would then decode it differently, splitting
|
|
17
|
+
`inputsHash`. The SDK now masks identically. Pinned on both sides so it cannot drift again:
|
|
18
|
+
`decodeScalarParam('HexColor', 0x1ffffffn) === '#ffffff'` (SDK) and the twin `test_DecodeScalars`
|
|
19
|
+
assertion in `contracts/test/FieldRenderer.t.sol`.
|
|
20
|
+
|
|
21
|
+
**SDK — gateway-prefix projection no longer trims (L-7).** `projectGatewayPrefix` dropped the `.trim()`
|
|
22
|
+
on the on-chain `abx_gateway_*` value: the deployed `_gatewayPrefix` returns the stored bytes verbatim,
|
|
23
|
+
so trimming here made the two planes disagree on a whitespace-padded prefix. The field store rejects an
|
|
24
|
+
empty value, so a set field is always non-empty. Clean prefixes (every real one) are unaffected.
|
|
25
|
+
|
|
26
|
+
**SDK — `permissionlessSalt` docstring hardened (L-3).** Now states explicitly: never use it for a
|
|
27
|
+
pre-published or pre-funded address — a third party can occupy the predicted address first with their own
|
|
28
|
+
`InitParams`. For any reserved address, use `saltFor` (its deployer-bound prefix is front-run-proof). No
|
|
29
|
+
behavior change; the shipped CLI/skill already deploy through `saltFor`.
|
|
30
|
+
|
|
31
|
+
**CLI — the edition schema advisory calls out a holder-writable `seed` by name (L-2).**
|
|
32
|
+
`editionSchemaAdvisory` already warned that a `TokenOwner` param on an edition is shared per-id
|
|
33
|
+
(last-writer-wins); it now adds a sharper note when that param is `seed`, because the normally-immutable
|
|
34
|
+
generative seed becomes re-rollable by _any_ holder of the id — changing the artwork for every
|
|
35
|
+
co-holder. The on-chain behavior is deliberately unchanged (the reassignable-seed feature is
|
|
36
|
+
buyer-verifiable via `paramSchema("seed")`); the guardrail is this warning, not a contract restriction.
|
|
37
|
+
A controller contract on the `Address` leg remains the way to govern it.
|
|
38
|
+
|
|
39
|
+
- 84ca9dd: Finish the burn + royalty-cap surfacing, from the 2026-08-20 w6 agent sandbox sweep. All off-chain —
|
|
40
|
+
no contract changed, no address moved; the on-chain features were verified working end to end (a cold
|
|
41
|
+
agent deployed `--burnable --royalty-cap 42`, minted, burned, and proved the cap enforced on Sepolia).
|
|
42
|
+
The sweep's convergent finding was that the two new **permanent** deploy-time decisions were invisible
|
|
43
|
+
after you set them; this makes them visible everywhere they're decided or read, and adds the owner's
|
|
44
|
+
missing reduce lever.
|
|
45
|
+
|
|
46
|
+
- **New: `abx set-royalty-cap <address> --cap <0-10000>`** — the owner's reduce-only royalty-ceiling
|
|
47
|
+
lever (`RoyaltyExtension.reduceMaxRoyaltyBps`), the "after" the deploy-time "reduce-only after"
|
|
48
|
+
promise never had. Both ways the chain refuses are checked locally before signing: a value that
|
|
49
|
+
wouldn't decrease (`RoyaltyCapNotReduced`) or that would drop below the live royalty rate
|
|
50
|
+
(`RoyaltyCapBelowRoyalty`). SDK: `prepareReduceMaxRoyaltyBps`.
|
|
51
|
+
- **The deploy readout now shows `royalty cap` and `burnable` on every lane** (1/1, series, code, and
|
|
52
|
+
all three edition twins), whenever set. Critically it flags an **auto-raised** cap
|
|
53
|
+
(`royalty cap 25% (auto-raised to fit the royalty)`): omitting `--royalty-cap` while setting a
|
|
54
|
+
royalty above 10% silently raises the permanent ceiling to match, which was previously invisible.
|
|
55
|
+
- **`abx state` now surfaces `royaltyCap` and `burnable`** (human + `--json`), the post-deploy
|
|
56
|
+
verification surface, for both the 721 and edition readouts.
|
|
57
|
+
- **The skill documents the royalty cap** — the identity decision (`decisions.md`), the confirm-
|
|
58
|
+
identity step (`SKILL.md`), and a new `set-royalty-cap` owner-op row (`operating.md`); previously the
|
|
59
|
+
reduce-only ceiling feature had zero skill coverage.
|
|
60
|
+
- **Help/doc corrections:** `set-royalty --help` range `0-1000`→`0-10000` (stale from the hard-cap
|
|
61
|
+
era); `deploy-series --help` now lists `--royalty-cap`/`--burnable`; `deploy --help` states the cap
|
|
62
|
+
auto-raises; `abx inspect`'s trait-feasibility deep-dive points at public docs, not a repo path.
|
|
63
|
+
|
|
64
|
+
- Updated dependencies [84ca9dd]
|
|
65
|
+
- Updated dependencies [84ca9dd]
|
|
66
|
+
- @artblocks/abx-sdk@0.1.0-alpha.20
|
|
67
|
+
- @artblocks/abx-indexer@0.1.0-alpha.21
|
|
68
|
+
- @artblocks/abx-storage@0.1.0-alpha.20
|
|
69
|
+
- @artblocks/abx-token-api@0.1.0-alpha.23
|
|
70
|
+
|
|
71
|
+
## 0.1.0-alpha.27
|
|
72
|
+
|
|
73
|
+
### Patch Changes
|
|
74
|
+
|
|
75
|
+
- b1c333d: **Asking for a 15% royalty built a transaction that reverted on chain.** `RoyaltyExtension` caps every
|
|
76
|
+
ABX token at **1000 bps (10%)** and reverts `RoyaltyTooHigh()` above it, and no token type overrides that.
|
|
77
|
+
But client-side validation allowed the full ERC-2981 `0–10000` range — and the CLI's own error text
|
|
78
|
+
offered `10000 = 100%` as a worked example. So a creator asking for an ordinary 15% got a fully built,
|
|
79
|
+
fully signed transaction that failed on chain; on the wallet lane they approved it in their own wallet
|
|
80
|
+
and paid the gas to find out. Both usage strings said `<0-10000>`, and so did two public docs.
|
|
81
|
+
|
|
82
|
+
Now `MAX_ROYALTY_BPS` (SDK) mirrors the contract ceiling and both `prepareSetRoyalty` and the CLI's
|
|
83
|
+
`parseRoyaltyBps` validate against it, so the refusal is instant, local and free, and it names the cap
|
|
84
|
+
and the revert. Regression tests cover it from both sides — including `1500`, the value that used to
|
|
85
|
+
sail through. Two existing tests had to be corrected: they asserted the 0–10000 bound, which is how this
|
|
86
|
+
survived.
|
|
87
|
+
|
|
88
|
+
Three more surfaces that contradicted themselves or the chain:
|
|
89
|
+
|
|
90
|
+
- **`abx ping-uri --help` claimed "Permissionless by design … ANY signer may call it (no owner check)"
|
|
91
|
+
four lines below its own `OWNER-ONLY` header** — and `Uri1155.pingURI` is `onlyOwner`. A non-owner
|
|
92
|
+
following the paragraph built a reverting tx.
|
|
93
|
+
- **`site/content/docs/reference/contracts.mdx`** described the pre-audit library architecture: it omitted
|
|
94
|
+
`AbxMetadataLib` (the fourth delegatecalled library, and the one linked by **all six** token types, not
|
|
95
|
+
three) and still said `SeriesCode` "rides the ceiling", where `deployments.mdx` — written after the same
|
|
96
|
+
redeploy — says both code types now sit under EIP-170 with room to spare.
|
|
97
|
+
- **`deployments.mdx`'s own `specVersion()` example printed `# → 10`** under a table declaring the renderer
|
|
98
|
+
spec v11. And **`operate.mdx`'s self-correction had rotted**: it warned that `abx refresh`'s advisory was
|
|
99
|
+
stale about editions and ERC-4906, but that advisory was fixed — the doc was now the stale one.
|
|
100
|
+
|
|
101
|
+
Found by two read-only audits (29 doc files, 19 spec files) that verified every claim against contracts,
|
|
102
|
+
compiled ABIs, SDK and live `--help` output before reporting. Both independently re-confirmed the
|
|
103
|
+
no-burn property, the v11 gateway design, all ~26 canonical addresses, and the on-chain storage
|
|
104
|
+
thresholds.
|
|
105
|
+
|
|
106
|
+
- b1c333d: **Every deploy lane accepted a royalty the chain would refuse.** `set-royalty` was fixed to validate
|
|
107
|
+
against the contract's 10% cap, but all six deploy paths (`deploy`, `deploy-series`, `deploy-code` and
|
|
108
|
+
their edition twins) still did a bare `Number(flags['royalty-bps'] ?? 500)` with no bound at all. So
|
|
109
|
+
`--royalty-bps 1500` built the whole deploy, printed **`royalty 15%`** in the confirmation readout — a
|
|
110
|
+
promise the chain will not keep — and reverted `RoyaltyTooHigh()` on send. That is the worse half of the
|
|
111
|
+
bug: the deploy is the expensive transaction, and on the wallet lane the creator approves it first.
|
|
112
|
+
|
|
113
|
+
All six now route through the same `parseRoyaltyBps` the owner op uses, so an over-cap value is refused
|
|
114
|
+
locally, for free, with the cap and the revert named.
|
|
115
|
+
|
|
116
|
+
**`abx doctor`'s pruned-history warning is now order-aware.** It fired for any reachable endpoint that
|
|
117
|
+
cannot serve archive-depth logs, even when a healthy archive endpoint sat ahead of it in the list and would
|
|
118
|
+
answer every deep scan. Three eval rooms independently called it noise — one said it was "a poor first
|
|
119
|
+
impression for an already-nervous creator". The hazard it exists for is real but specifically about
|
|
120
|
+
_ordering_: the fallback transport rotates on error only, so a pruning endpoint listed **first** answers a
|
|
121
|
+
deep scan with an empty, successful `[]`. `probeRpcEndpoints` preserves list order, so the warning now
|
|
122
|
+
fires only when the pruning endpoint is ahead of a full-archive one — the case a reader can act on. When
|
|
123
|
+
no archive endpoint is configured at all, it still fires for every pruning endpoint, because then nothing
|
|
124
|
+
shadows them.
|
|
125
|
+
|
|
126
|
+
- b1c333d: An agent using this skill built its own smart-contract system, and neither it nor the creator noticed
|
|
127
|
+
that the collection had stopped being a canonical ABX clone. Canonicity is decided by which contract
|
|
128
|
+
deployed a token and can never be added afterwards, so the fix is a gate before the first send plus a
|
|
129
|
+
readout that makes the loss discoverable.
|
|
130
|
+
|
|
131
|
+
**`abx state` now reports canonicity first.** It read owner, supply, royalty and renderer off any
|
|
132
|
+
ERC-721-shaped address and said nothing about whether it was a factory clone — so a hand-rolled contract
|
|
133
|
+
was described exactly as confidently as a canonical one, and the tool whose whole job is "what IS this
|
|
134
|
+
contract?" left nothing behind for the creator to find. `verifyCanonical` (SDK) probes all six trust
|
|
135
|
+
anchors' `isAbxClone` in one multicall and returns a true tri-state; a `false` prints what was lost, that
|
|
136
|
+
marketplaces and the App Store recognize collections by that signal, and that the only remedy is a fresh
|
|
137
|
+
deploy through the factory plus moving holders. `null` (no anchors on this chain, or an RPC failure) is
|
|
138
|
+
never shown as a "no".
|
|
139
|
+
|
|
140
|
+
**The skill gained the boundary rule it never had.** Its trust material lived on the _last line_ of the
|
|
141
|
+
file, written for someone verifying a clone from outside, and the "don't hand-roll" rules that existed
|
|
142
|
+
were narrow — preview pages, ABI writes, token queries, nothing about contracts. There is now a
|
|
143
|
+
Read-first gate: if the ask seems to need something the deploy commands don't do, stop and put it to the
|
|
144
|
+
creator in words, because building your own contracts is a legitimate choice a creator can make and
|
|
145
|
+
making it _for_ them silently is not. Alongside it, the list of what the toolkit does **not** do — no
|
|
146
|
+
burn (verified: zero `burn` entrypoints in `contracts/src`, previously a parenthetical clause inside a
|
|
147
|
+
sentence about hooks), no secondary listings, no mainnet, fixed-price sales only, no post-deploy script
|
|
148
|
+
replace — and the two extension points that keep a collection canonical (`set-minter`, param hooks), with
|
|
149
|
+
the caveat that a `--transfer` hook is a **veto, not a trigger**: it can refuse a transfer but never cause
|
|
150
|
+
a mint or a burn, so it cannot implement "combine two into one".
|
|
151
|
+
|
|
152
|
+
Verified by two trap rooms — a creator asking for a burn-to-combine mechanic with "you've got my
|
|
153
|
+
go-ahead, I don't need the technical parts". Both Sonnet and Haiku refused to build a contract, cited the
|
|
154
|
+
canonicity loss, and returned options. The Sonnet room found the route that _preserves_ canonicity and
|
|
155
|
+
offered it first.
|
|
156
|
+
|
|
157
|
+
**SKILL.md was restructured** against its own "lean decision-tree router" norm, which it had drifted
|
|
158
|
+
~40% past: **68,449 → 55,392 characters (−20%)**, longest bullet 2,876 → 1,465, bullets over 600 chars
|
|
159
|
+
27 → 14, with depth moved into the reference files that already owned it (locks → `decisions.md`;
|
|
160
|
+
install/`.env` → `setup.md`, which was a 44-line stub; data plane, App Store and deploy mechanics →
|
|
161
|
+
`operating.md`). Also repaired: a **markdown table split in two** by a blockquote sitting between its
|
|
162
|
+
rows, and **four dangling anchor links** (three pre-existing) — all 96 internal links now resolve.
|
|
163
|
+
`setup.md` was retitled and reordered after a control room found the required install steps buried under
|
|
164
|
+
a title that promised RPC troubleshooting.
|
|
165
|
+
|
|
166
|
+
**Two contradictions the verification sweep caught**, both the same class as the rest of this week's
|
|
167
|
+
fixes. `deploy-code --dry-run` printed `render/storage ✓ fs + --image-base` a few lines below
|
|
168
|
+
`⚠ render storage home is fs … placeholder forever`, for the identical setup; the ✓ is legitimately
|
|
169
|
+
scoped to whether the `--image-base` target is a valid mutable bucket (two tests pin that), so the row now
|
|
170
|
+
states what it covers and points at the other line rather than appearing to overrule it. And **"not
|
|
171
|
+
backfillable" was overstated** in three places — the truth, which `code-projects.md` had right all along,
|
|
172
|
+
is that fixing a surface after deploy costs an owner-signed re-point plus a re-render on a collection that
|
|
173
|
+
showed a placeholder in between. Expensive and worth avoiding; not impossible.
|
|
174
|
+
|
|
175
|
+
- Updated dependencies [b1c333d]
|
|
176
|
+
- Updated dependencies [b1c333d]
|
|
177
|
+
- @artblocks/abx-sdk@0.1.0-alpha.19
|
|
178
|
+
- @artblocks/abx-indexer@0.1.0-alpha.20
|
|
179
|
+
- @artblocks/abx-storage@0.1.0-alpha.19
|
|
180
|
+
- @artblocks/abx-token-api@0.1.0-alpha.22
|
|
181
|
+
|
|
3
182
|
## 0.1.0-alpha.26
|
|
4
183
|
|
|
5
184
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAC,eAAe,EAAC,MAAM,wBAAwB,CAAC;AACvD,OAAO,EACL,KAAK,OAAO,EAMZ,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,YAAY,EAKjB,KAAK,qBAAqB,EAmD3B,MAAM,oBAAoB,CAAC;AAY5B,OAAO,EAAC,KAAK,GAAG,EAAyC,MAAM,MAAM,CAAC;AACtE,OAAO,EAGL,KAAK,gBAAgB,EAmBtB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAyB,KAAK,KAAK,EAA8E,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/deploy.js
CHANGED
|
@@ -26,7 +26,7 @@ import { isEditionContract } from '../kind.js';
|
|
|
26
26
|
import { jsonSafe, withJson } from '../jsonout.js';
|
|
27
27
|
import { planOnChainScript } from '../script-chunks.js';
|
|
28
28
|
import { bold, c, dim, ensureFactory, ensureRenderer, ensureSeedSource, ensureSeriesCodeFactory, ensureSeriesFactory, ensureOneOfOneEditionFactory, ensureEditionFactory, ensureEditionCodeFactory, g, info, keepAlive, ok, p, portInUse, printServing, registerAndIndexLocally, reindexAfterDeploy, step, warn, } from '../output.js';
|
|
29
|
-
import { AUTHORSHIP_DEPLOY_FIELDS, ONCHAIN_PROJECT_SOFT_LIMIT, ONCHAIN_READ_WARN_BYTES, tokenUriGasEstimate, authorshipContractFields, gatewayContractFields, servingGateway, computeContentPlan, envStagingSender, parseCompress, parseSeedSourceValue, canonicalSeedSource, refuseUnusableSeedSource, parseTransferValidatorValue, guardOnChainSize, previewImageStaging, refusePrewrappedImage, sessionStagingSender, stageImageField, stageImageFieldsBatch, } from '../ownerops.js';
|
|
29
|
+
import { parseRoyaltyBps, AUTHORSHIP_DEPLOY_FIELDS, ONCHAIN_PROJECT_SOFT_LIMIT, ONCHAIN_READ_WARN_BYTES, tokenUriGasEstimate, authorshipContractFields, gatewayContractFields, servingGateway, computeContentPlan, envStagingSender, parseCompress, parseSeedSourceValue, canonicalSeedSource, refuseUnusableSeedSource, parseTransferValidatorValue, guardOnChainSize, previewImageStaging, refusePrewrappedImage, sessionStagingSender, stageImageField, stageImageFieldsBatch, } from '../ownerops.js';
|
|
30
30
|
import { canonicalLabel } from '../remote.js';
|
|
31
31
|
import { assertLaneCanSign, confirmSend, gatedSend, laneFromFlags } from '../riskgate.js';
|
|
32
32
|
import { describeSchema, editionSchemaAdvisory, parseSchemaSpecs } from '../schema.js';
|
|
@@ -188,6 +188,8 @@ export function deployCommandLine(flags, salt) {
|
|
|
188
188
|
bool('onchain-image');
|
|
189
189
|
str('compress', flags.compress);
|
|
190
190
|
str('royalty-bps', flags['royalty-bps']);
|
|
191
|
+
str('royalty-cap', flags['royalty-cap']);
|
|
192
|
+
bool('burnable');
|
|
191
193
|
bool('no-mint');
|
|
192
194
|
str('721c', flags['721c'] === 'true' ? 'recommended' : flags['721c']); // bare --721c ≡ recommended
|
|
193
195
|
str('backend', flags.backend);
|
|
@@ -237,6 +239,8 @@ export function deploySeriesCommandLine(flags, salt) {
|
|
|
237
239
|
str('minter', flags.minter);
|
|
238
240
|
str('primary-payee', flags['primary-payee']);
|
|
239
241
|
str('royalty-bps', flags['royalty-bps']);
|
|
242
|
+
str('royalty-cap', flags['royalty-cap']);
|
|
243
|
+
bool('burnable');
|
|
240
244
|
str('721c', flags['721c'] === 'true' ? 'recommended' : flags['721c']); // bare --721c ≡ recommended
|
|
241
245
|
if (flags.sign !== undefined)
|
|
242
246
|
parts.push('--sign');
|
|
@@ -279,6 +283,8 @@ export function deployCodeCommandLine(flags, salt) {
|
|
|
279
283
|
str('minter', flags.minter);
|
|
280
284
|
str('primary-payee', flags['primary-payee']);
|
|
281
285
|
str('royalty-bps', flags['royalty-bps']);
|
|
286
|
+
str('royalty-cap', flags['royalty-cap']);
|
|
287
|
+
bool('burnable');
|
|
282
288
|
str('721c', flags['721c'] === 'true' ? 'recommended' : flags['721c']); // bare --721c ≡ recommended
|
|
283
289
|
str('backend', flags.backend);
|
|
284
290
|
str('gateway', flags.gateway);
|
|
@@ -634,7 +640,19 @@ export async function cmdDeployBody(flags, serveAfter, emit) {
|
|
|
634
640
|
}
|
|
635
641
|
const name = flags.name ?? 'ABX Self-Host Demo';
|
|
636
642
|
const symbol = flags.symbol ?? 'ABXSH';
|
|
637
|
-
|
|
643
|
+
// Validate against the CONTRACT's cap here, not just in `set-royalty`: a bare Number() accepted
|
|
644
|
+
// 1500, printed "royalty 15%" in the confirmation readout, and then lost the whole deploy to a
|
|
645
|
+
// RoyaltyTooHigh() revert — the expensive tx, and one the creator had already approved.
|
|
646
|
+
const royaltyBps = flags['royalty-bps'] === undefined ? 500 : parseRoyaltyBps(String(flags['royalty-bps']));
|
|
647
|
+
// B45: owner-set royalty ceiling (bps, up to 100%), reduce-only after deploy. Defaults to 10%,
|
|
648
|
+
// auto-raised to fit a higher --royalty-bps so a plausible input never errors; --royalty-cap overrides.
|
|
649
|
+
const maxRoyaltyBps = flags['royalty-cap'] === undefined
|
|
650
|
+
? Math.max(1000, royaltyBps)
|
|
651
|
+
: parseRoyaltyBps(String(flags['royalty-cap']));
|
|
652
|
+
if (royaltyBps > maxRoyaltyBps) {
|
|
653
|
+
throw new Error(`--royalty-bps ${royaltyBps / 100}% exceeds --royalty-cap ${maxRoyaltyBps / 100}%; raise the cap or lower the royalty.`);
|
|
654
|
+
}
|
|
655
|
+
const burnable = flags.burnable === '' || flags.burnable === 'true';
|
|
638
656
|
const port = Number(flags.port ?? process.env.ABX_PORT ?? DEFAULT_PORT);
|
|
639
657
|
const baseUrl = flags['public-base-url'] ?? resolveBaseUrl(port);
|
|
640
658
|
// Creator traits (real OpenSea attributes). Off-chain operator metadata by default (cheap,
|
|
@@ -949,6 +967,8 @@ export async function cmdDeployBody(flags, serveAfter, emit) {
|
|
|
949
967
|
contractURIRenderer: renderer,
|
|
950
968
|
royaltyReceiver: deployer,
|
|
951
969
|
royaltyBps,
|
|
970
|
+
maxRoyaltyBps,
|
|
971
|
+
burnable,
|
|
952
972
|
transferValidator,
|
|
953
973
|
tokenFields,
|
|
954
974
|
// authorship + rights (creator / license / …) are collection-scope on-chain fields.
|
|
@@ -988,6 +1008,8 @@ export async function cmdDeployBody(flags, serveAfter, emit) {
|
|
|
988
1008
|
authorshipReadout(flags).forEach((line) => info(line));
|
|
989
1009
|
info(describeTraits(traits, traitsOnchain));
|
|
990
1010
|
info(`royalty ${royaltyBps / 100}% → ${deployer}${flags['royalty-bps'] ? '' : dim(' (default 5%)')}`);
|
|
1011
|
+
info(`royalty cap ${maxRoyaltyBps / 100}%${flags['royalty-cap'] !== undefined ? '' : maxRoyaltyBps > 1000 ? dim(' (auto-raised to fit the royalty)') : dim(' (default 10%)')} ${dim('— reduce-only ceiling; lower later with `abx set-royalty-cap`')}`);
|
|
1012
|
+
info(`burnable ${burnable ? `yes ${dim('— holders may burn their own token')}` : `no ${dim('(no token can be destroyed)')}`}`);
|
|
991
1013
|
info(contentNote);
|
|
992
1014
|
info(`tokenURI base ${params.tokenURIBase || dim('(empty — resolves on-chain via the renderer)')}`);
|
|
993
1015
|
// Same reasoning as above: this line derives token #0's URL from `predicted`, so it's only an
|
|
@@ -1277,7 +1299,16 @@ export async function cmdDeployOneOfOneEditionBody(flags, emit) {
|
|
|
1277
1299
|
const editionSize = parseCopies(flags.copies);
|
|
1278
1300
|
const name = flags.name ?? 'ABX Edition';
|
|
1279
1301
|
const symbol = flags.symbol ?? 'ABXE';
|
|
1280
|
-
const royaltyBps =
|
|
1302
|
+
const royaltyBps = flags['royalty-bps'] === undefined ? 500 : parseRoyaltyBps(String(flags['royalty-bps']));
|
|
1303
|
+
// B45: owner-set royalty ceiling (bps, up to 100%), reduce-only after deploy. Defaults to 10%,
|
|
1304
|
+
// auto-raised to fit a higher --royalty-bps so a plausible input never errors; --royalty-cap overrides.
|
|
1305
|
+
const maxRoyaltyBps = flags['royalty-cap'] === undefined
|
|
1306
|
+
? Math.max(1000, royaltyBps)
|
|
1307
|
+
: parseRoyaltyBps(String(flags['royalty-cap']));
|
|
1308
|
+
if (royaltyBps > maxRoyaltyBps) {
|
|
1309
|
+
throw new Error(`--royalty-bps ${royaltyBps / 100}% exceeds --royalty-cap ${maxRoyaltyBps / 100}%; raise the cap or lower the royalty.`);
|
|
1310
|
+
}
|
|
1311
|
+
const burnable = flags.burnable === '' || flags.burnable === 'true';
|
|
1281
1312
|
const port = Number(flags.port ?? process.env.ABX_PORT ?? DEFAULT_PORT);
|
|
1282
1313
|
const baseUrl = flags['public-base-url'] ?? resolveBaseUrl(port);
|
|
1283
1314
|
const traits = parseDeployTraits(flags);
|
|
@@ -1443,6 +1474,8 @@ export async function cmdDeployOneOfOneEditionBody(flags, emit) {
|
|
|
1443
1474
|
contractURIRenderer: renderer,
|
|
1444
1475
|
royaltyReceiver: deployer,
|
|
1445
1476
|
royaltyBps,
|
|
1477
|
+
maxRoyaltyBps,
|
|
1478
|
+
burnable,
|
|
1446
1479
|
transferValidator,
|
|
1447
1480
|
editionSize,
|
|
1448
1481
|
primaryPayee,
|
|
@@ -1481,6 +1514,8 @@ export async function cmdDeployOneOfOneEditionBody(flags, emit) {
|
|
|
1481
1514
|
authorshipReadout(flags).forEach((line) => info(line));
|
|
1482
1515
|
info(describeTraits(traits, traitsOnchain));
|
|
1483
1516
|
info(`royalty ${royaltyBps / 100}% → ${deployer}${flags['royalty-bps'] ? '' : dim(' (default 5%)')}`);
|
|
1517
|
+
info(`royalty cap ${maxRoyaltyBps / 100}%${flags['royalty-cap'] !== undefined ? '' : maxRoyaltyBps > 1000 ? dim(' (auto-raised to fit the royalty)') : dim(' (default 10%)')} ${dim('— reduce-only ceiling; lower later with `abx set-royalty-cap`')}`);
|
|
1518
|
+
info(`burnable ${burnable ? `yes ${dim('— holders may burn their own token')}` : `no ${dim('(no token can be destroyed)')}`}`);
|
|
1484
1519
|
info(contentNote);
|
|
1485
1520
|
info(`tokenURI base ${params.tokenURIBase || dim('(empty — resolves on-chain via the renderer)')}`);
|
|
1486
1521
|
info(`contractURI base ${params.contractURIBase || dim('(empty — resolves on-chain via the renderer)')}`);
|
|
@@ -1638,7 +1673,16 @@ export async function cmdDeploySeriesBody(flags, emit) {
|
|
|
1638
1673
|
warnSignWithoutFor(flags);
|
|
1639
1674
|
const name = flags.name ?? 'ABX Series';
|
|
1640
1675
|
const symbol = flags.symbol ?? 'ABXS';
|
|
1641
|
-
const royaltyBps =
|
|
1676
|
+
const royaltyBps = flags['royalty-bps'] === undefined ? 500 : parseRoyaltyBps(String(flags['royalty-bps']));
|
|
1677
|
+
// B45: owner-set royalty ceiling (bps, up to 100%), reduce-only after deploy. Defaults to 10%,
|
|
1678
|
+
// auto-raised to fit a higher --royalty-bps so a plausible input never errors; --royalty-cap overrides.
|
|
1679
|
+
const maxRoyaltyBps = flags['royalty-cap'] === undefined
|
|
1680
|
+
? Math.max(1000, royaltyBps)
|
|
1681
|
+
: parseRoyaltyBps(String(flags['royalty-cap']));
|
|
1682
|
+
if (royaltyBps > maxRoyaltyBps) {
|
|
1683
|
+
throw new Error(`--royalty-bps ${royaltyBps / 100}% exceeds --royalty-cap ${maxRoyaltyBps / 100}%; raise the cap or lower the royalty.`);
|
|
1684
|
+
}
|
|
1685
|
+
const burnable = flags.burnable === '' || flags.burnable === 'true';
|
|
1642
1686
|
const port = Number(flags.port ?? process.env.ABX_PORT ?? DEFAULT_PORT);
|
|
1643
1687
|
const baseUrl = flags['public-base-url'] ?? resolveBaseUrl(port);
|
|
1644
1688
|
console.log(bold(`\n ABX Self-Host Toolkit — deploy series\n ${dim('a multi-token drop, served from chain alone')}`));
|
|
@@ -1945,6 +1989,8 @@ export async function cmdDeploySeriesBody(flags, emit) {
|
|
|
1945
1989
|
contractURIRenderer: renderer,
|
|
1946
1990
|
royaltyReceiver: deployer,
|
|
1947
1991
|
royaltyBps,
|
|
1992
|
+
maxRoyaltyBps,
|
|
1993
|
+
burnable,
|
|
1948
1994
|
transferValidator,
|
|
1949
1995
|
maxInvocations: count,
|
|
1950
1996
|
primaryPayee,
|
|
@@ -1980,7 +2026,7 @@ export async function cmdDeploySeriesBody(flags, emit) {
|
|
|
1980
2026
|
// readout for the no-salt case).
|
|
1981
2027
|
if (explicitSalt)
|
|
1982
2028
|
info(`deterministic address: ${predicted}`);
|
|
1983
|
-
info(`name "${name}" · symbol ${symbol} · size ${count} · royalty ${royaltyBps / 100}
|
|
2029
|
+
info(`name "${name}" · symbol ${symbol} · size ${count} · royalty ${royaltyBps / 100}% · cap ${maxRoyaltyBps / 100}%${burnable ? ' · burnable' : ''}`);
|
|
1984
2030
|
authorshipReadout(flags).forEach((line) => info(line));
|
|
1985
2031
|
info(`mint: ${mintCount > 0 ? `${mintCount} token(s) in order → ${deployer} at deploy` : 'deferred (mint later / external minter)'}`);
|
|
1986
2032
|
if (minter !== zeroAddress)
|
|
@@ -2199,7 +2245,16 @@ export async function cmdDeployEditionImageBody(flags, emit) {
|
|
|
2199
2245
|
const editionSize = parseCopies(flags.copies);
|
|
2200
2246
|
const name = flags.name ?? 'ABX Edition Series';
|
|
2201
2247
|
const symbol = flags.symbol ?? 'ABXES';
|
|
2202
|
-
const royaltyBps =
|
|
2248
|
+
const royaltyBps = flags['royalty-bps'] === undefined ? 500 : parseRoyaltyBps(String(flags['royalty-bps']));
|
|
2249
|
+
// B45: owner-set royalty ceiling (bps, up to 100%), reduce-only after deploy. Defaults to 10%,
|
|
2250
|
+
// auto-raised to fit a higher --royalty-bps so a plausible input never errors; --royalty-cap overrides.
|
|
2251
|
+
const maxRoyaltyBps = flags['royalty-cap'] === undefined
|
|
2252
|
+
? Math.max(1000, royaltyBps)
|
|
2253
|
+
: parseRoyaltyBps(String(flags['royalty-cap']));
|
|
2254
|
+
if (royaltyBps > maxRoyaltyBps) {
|
|
2255
|
+
throw new Error(`--royalty-bps ${royaltyBps / 100}% exceeds --royalty-cap ${maxRoyaltyBps / 100}%; raise the cap or lower the royalty.`);
|
|
2256
|
+
}
|
|
2257
|
+
const burnable = flags.burnable === '' || flags.burnable === 'true';
|
|
2203
2258
|
const port = Number(flags.port ?? process.env.ABX_PORT ?? DEFAULT_PORT);
|
|
2204
2259
|
const baseUrl = flags['public-base-url'] ?? resolveBaseUrl(port);
|
|
2205
2260
|
console.log(bold(`\n ABX Self-Host Toolkit — deploy edition series\n ${dim(editionSize === 0n
|
|
@@ -2486,6 +2541,8 @@ export async function cmdDeployEditionImageBody(flags, emit) {
|
|
|
2486
2541
|
contractURIRenderer: renderer,
|
|
2487
2542
|
royaltyReceiver: deployer,
|
|
2488
2543
|
royaltyBps,
|
|
2544
|
+
maxRoyaltyBps,
|
|
2545
|
+
burnable,
|
|
2489
2546
|
transferValidator,
|
|
2490
2547
|
maxInvocations: count,
|
|
2491
2548
|
editionSize,
|
|
@@ -2526,6 +2583,8 @@ export async function cmdDeployEditionImageBody(flags, emit) {
|
|
|
2526
2583
|
info(`ids ${count} (id-space cap)`);
|
|
2527
2584
|
info(`copies ${editionSize === 0n ? 'open (uncapped) per id' : `${editionSize} per id`}`);
|
|
2528
2585
|
info(`royalty ${royaltyBps / 100}% → ${deployer}${flags['royalty-bps'] ? '' : dim(' (default 5%)')}`);
|
|
2586
|
+
info(`royalty cap ${maxRoyaltyBps / 100}%${flags['royalty-cap'] !== undefined ? '' : maxRoyaltyBps > 1000 ? dim(' (auto-raised to fit the royalty)') : dim(' (default 10%)')} ${dim('— reduce-only ceiling; lower later with `abx set-royalty-cap`')}`);
|
|
2587
|
+
info(`burnable ${burnable ? `yes ${dim('— holders may burn their own token')}` : `no ${dim('(no token can be destroyed)')}`}`);
|
|
2529
2588
|
info(`tokenURI base ${params.tokenURIBase || dim('(empty — resolves on-chain via the renderer)')}`);
|
|
2530
2589
|
info(`mint: ${effectiveMintCount > 0 ? `${effectiveMintCount} id(s) × ${mintAmount} cop${mintAmount === 1n ? 'y' : 'ies'} → ${deployer} at deploy` : 'deferred'}`);
|
|
2531
2590
|
if (minter !== zeroAddress)
|
|
@@ -2846,7 +2905,7 @@ export const DEPLOY_CODE_FLAGS = new Set([
|
|
|
2846
2905
|
...AUTHORSHIP_DEPLOY_FIELDS.map(([flag]) => flag), // creator · display-notes · creator-links · license
|
|
2847
2906
|
'image-base', 'attributes-renderer', 'image-renderer',
|
|
2848
2907
|
// supply + mint + economics
|
|
2849
|
-
'max', 'mint-count', 'mint-all', 'no-mint', 'unpaused', 'minter', 'primary-payee', 'royalty-bps', '721c',
|
|
2908
|
+
'max', 'mint-count', 'mint-all', 'no-mint', 'unpaused', 'minter', 'primary-payee', 'royalty-bps', 'royalty-cap', 'burnable', '721c',
|
|
2850
2909
|
// params + dependencies
|
|
2851
2910
|
'schema', 'no-seed', 'seed-source', 'no-delegation', 'dep', 'dep-registry',
|
|
2852
2911
|
// serving lane
|
|
@@ -2875,13 +2934,13 @@ export const SHARED_DEPLOY_FLAGS = [
|
|
|
2875
2934
|
// A 1/1 `abx deploy` accepts these (see `abx help deploy`). Anything else warns (typo'd/unsupported).
|
|
2876
2935
|
export const DEPLOY_FLAGS = new Set([
|
|
2877
2936
|
'image', 'name', 'symbol', 'type', 'description', 'description-onchain', 'external-url', 'traits', 'traits-onchain', 'attributes',
|
|
2878
|
-
'onchain-uri', 'onchain-image', 'compress', 'allow-unreadable-onchain', 'royalty-bps', 'no-mint',
|
|
2937
|
+
'onchain-uri', 'onchain-image', 'compress', 'allow-unreadable-onchain', 'royalty-bps', 'royalty-cap', 'burnable', 'no-mint',
|
|
2879
2938
|
...AUTHORSHIP_DEPLOY_FIELDS.map(([flag]) => flag),
|
|
2880
2939
|
...SHARED_DEPLOY_FLAGS,
|
|
2881
2940
|
]);
|
|
2882
2941
|
// `abx deploy-series` accepts these (see `abx help deploy-series`).
|
|
2883
2942
|
export const DEPLOY_SERIES_FLAGS = new Set([
|
|
2884
|
-
'dir', 'count', 'name', 'symbol', 'description', 'external-url', 'royalty-bps', 'attributes', 'traits-onchain',
|
|
2943
|
+
'dir', 'count', 'name', 'symbol', 'description', 'external-url', 'royalty-bps', 'royalty-cap', 'burnable', 'attributes', 'traits-onchain',
|
|
2885
2944
|
'no-mint', 'mint-all', 'mint-count', 'unpaused', 'minter', 'primary-payee',
|
|
2886
2945
|
'onchain-uri', 'onchain-image', 'compress', 'allow-unreadable-onchain', 'chunk-store',
|
|
2887
2946
|
...AUTHORSHIP_DEPLOY_FIELDS.map(([flag]) => flag),
|
|
@@ -2952,7 +3011,16 @@ export async function cmdDeployCodeBody(flags, emit) {
|
|
|
2952
3011
|
if (mintCount > max)
|
|
2953
3012
|
throw new Error(`--mint-count ${mintCount} exceeds --max ${max}`);
|
|
2954
3013
|
const paused = flags.unpaused === undefined;
|
|
2955
|
-
const royaltyBps =
|
|
3014
|
+
const royaltyBps = flags['royalty-bps'] === undefined ? 500 : parseRoyaltyBps(String(flags['royalty-bps']));
|
|
3015
|
+
// B45: owner-set royalty ceiling (bps, up to 100%), reduce-only after deploy. Defaults to 10%,
|
|
3016
|
+
// auto-raised to fit a higher --royalty-bps so a plausible input never errors; --royalty-cap overrides.
|
|
3017
|
+
const maxRoyaltyBps = flags['royalty-cap'] === undefined
|
|
3018
|
+
? Math.max(1000, royaltyBps)
|
|
3019
|
+
: parseRoyaltyBps(String(flags['royalty-cap']));
|
|
3020
|
+
if (royaltyBps > maxRoyaltyBps) {
|
|
3021
|
+
throw new Error(`--royalty-bps ${royaltyBps / 100}% exceeds --royalty-cap ${maxRoyaltyBps / 100}%; raise the cap or lower the royalty.`);
|
|
3022
|
+
}
|
|
3023
|
+
const burnable = flags.burnable === '' || flags.burnable === 'true';
|
|
2956
3024
|
const port = Number(flags.port ?? process.env.ABX_PORT ?? DEFAULT_PORT);
|
|
2957
3025
|
const baseUrl = (flags['public-base-url'] ?? resolveBaseUrl(port)).replace(/\/$/, '');
|
|
2958
3026
|
const hasPublicUrl = !!(flags['public-base-url'] || process.env.ABX_PUBLIC_BASE_URL);
|
|
@@ -3434,7 +3502,7 @@ export async function cmdDeployCodeBody(flags, emit) {
|
|
|
3434
3502
|
(transferValidator !== zeroAddress ? ` ERC-721C: enrolled at deploy, permanently (validator ${transferValidator}).` : '') + `\n` +
|
|
3435
3503
|
` Surfaces — thumbnail: ${imageDisposition}. traits: ${traitsDisposition}. postparams: ${paramsDisposition}.` +
|
|
3436
3504
|
((imageOrphaned || traitsBroken || undeclaredParams.length)
|
|
3437
|
-
? `\n ⚠ One or more surfaces resolve to NOTHING marketplaces can see
|
|
3505
|
+
? `\n ⚠ One or more surfaces resolve to NOTHING marketplaces can see. Settle them now: fixing one after deploy is an owner-signed re-point + re-render, on a collection that showed a placeholder in between. Re-run --dry-run after fixing, or pass --yes to ship as-is.`
|
|
3438
3506
|
: ''), flags);
|
|
3439
3507
|
}
|
|
3440
3508
|
// --dry-run: emit the full plan (the on-chain values the skill formats into its confirm table),
|
|
@@ -3477,7 +3545,7 @@ export async function cmdDeployCodeBody(flags, emit) {
|
|
|
3477
3545
|
info(`deployer: ${dim('not set')} — pass ${bold('--for 0x..')} (or set a signing key) to preview the exact deterministic address; the plan below is otherwise deployer-independent.`);
|
|
3478
3546
|
if (shownAddr)
|
|
3479
3547
|
info(`deterministic address: ${shownAddr}`);
|
|
3480
|
-
info(`name "${name}" · symbol ${symbol} · max ${max}${maxProvided ? '' : ' ⚠ default — set --max N'} · royalty ${royaltyBps / 100}%${royaltyProvided ? '' : ' ⚠ default — set --royalty-bps N'}`);
|
|
3548
|
+
info(`name "${name}" · symbol ${symbol} · max ${max}${maxProvided ? '' : ' ⚠ default — set --max N'} · royalty ${royaltyBps / 100}%${royaltyProvided ? '' : ' ⚠ default — set --royalty-bps N'} · cap ${maxRoyaltyBps / 100}%${burnable ? ' · burnable' : ''}`);
|
|
3481
3549
|
if (!flags.description)
|
|
3482
3550
|
warn(`no --description — the on-chain metadata ships with no description (the one creators most often forget). Add --description "…" or deploy bare.`);
|
|
3483
3551
|
info(`content: ${contentSummary}`);
|
|
@@ -3567,16 +3635,18 @@ export async function cmdDeployCodeBody(flags, emit) {
|
|
|
3567
3635
|
// were already set. The alarm now covers only the surfaces it can honestly speak for, and names
|
|
3568
3636
|
// only the remedies for what is actually broken.
|
|
3569
3637
|
const anySurfaceBroken = imageOrphaned || traitsBroken;
|
|
3570
|
-
step('Surfaces — what marketplaces will see (
|
|
3638
|
+
step('Surfaces — what marketplaces will see (settle them NOW; fixing one later is an owner-signed re-point + re-render)');
|
|
3571
3639
|
(imageOrphaned ? warn : info)(`thumbnail: ${imageDisposition}`);
|
|
3572
3640
|
// Render-mode / render-home guidance is for the RENDERED-still lanes only. The in-chain
|
|
3573
3641
|
// Solidity image (--image-renderer) has no off-chain still — nothing to render, host, or refresh.
|
|
3642
|
+
// Hoisted: the `render/storage` row further down needs the same fact, so that a managed provider
|
|
3643
|
+
// (which owns rendering) never trips the fs warning there either.
|
|
3644
|
+
let renderAttached = false;
|
|
3574
3645
|
if (!imageOrphaned && !hasImageRenderer) {
|
|
3575
3646
|
if (!hasPublicUrl)
|
|
3576
3647
|
info(` ${dim('render mode (a still is never on-chain):')} ${bold('service')} (auto-render every mint/param change — a live/for-sale drop) · ${bold('once')} (fixed supply) · ${bold('none')} (placeholder). ${dim('freshness: no resolver ⇒ no chain-watcher ⇒ MANUAL/backfill (`abx render` after mints + each param change; the on-chain animation updates live, the bucket still does NOT). Continuous/live stills ⇒ run a resolver.')}`);
|
|
3577
3648
|
// A managed provider whose descriptor says `render.attached` owns rendering — local
|
|
3578
3649
|
// ABX_STORAGE_BACKEND is irrelevant, and warning "placeholder forever" is a lie.
|
|
3579
|
-
let renderAttached = false;
|
|
3580
3650
|
if (hasPublicUrl && !loopbackBaseUrl(baseUrl)) {
|
|
3581
3651
|
try {
|
|
3582
3652
|
const d = await new AbxServiceClient({ baseUrl, timeoutMs: 2_500 }).descriptor();
|
|
@@ -3642,8 +3712,24 @@ export async function cmdDeployCodeBody(flags, emit) {
|
|
|
3642
3712
|
cloudHasPublicBase: renderStorageBackendId === 'cloud' ? !!renderStorageOpts.cloud?.publicBase : undefined,
|
|
3643
3713
|
publishesToRemoteResolver: !hasImageRenderer && !(onChainUri && hasImageBase),
|
|
3644
3714
|
});
|
|
3645
|
-
|
|
3646
|
-
|
|
3715
|
+
// `validateRenderStorageCombo` deliberately scopes itself to the --image-base URL SHAPE and the
|
|
3716
|
+
// remote-resolver lane, so it returns ok for `fs` + --image-base — while the render-home check a
|
|
3717
|
+
// few lines above correctly warns that an fs home means nothing ever uploads the still to that
|
|
3718
|
+
// bucket. Both lines described the same setup and only one said it was broken, and a creator
|
|
3719
|
+
// skimming for the checkmark reads ✓ as approval. The row now defers to that warning rather than
|
|
3720
|
+
// overriding it: same fact, one verdict.
|
|
3721
|
+
// This row is deliberately scoped to whether the --image-base TARGET is valid (a mutable bucket,
|
|
3722
|
+
// not a content-addressed gateway) and whether a remote resolver could fetch what we store — it
|
|
3723
|
+
// is NOT a verdict on the render home, which has its own warning above. Unqualified, though, a
|
|
3724
|
+
// bare `✓ fs + --image-base` sat a few lines under "render storage home is fs … placeholder
|
|
3725
|
+
// forever" and read as though it overruled it. Name what the ✓ covers, and point at the other
|
|
3726
|
+
// line when that one is unhappy, so one setup stops producing two verdicts.
|
|
3727
|
+
const fsHomeUnresolved = renderHome === 'fs' && !renderAttached && !imageOrphaned && !hasImageRenderer;
|
|
3728
|
+
if (renderStorageCombo.ok) {
|
|
3729
|
+
const scope = hasImageBase ? '--image-base is a valid mutable target' : `${renderStorageBackendId} can serve what it stores`;
|
|
3730
|
+
info(`render/storage ✓ ${scope}` +
|
|
3731
|
+
(fsHomeUnresolved ? dim(` — but the render home is still ${bold('fs')}; see the warning above, this ✓ does not cover it`) : ''));
|
|
3732
|
+
}
|
|
3647
3733
|
else
|
|
3648
3734
|
warn(`render/storage ✗ ${renderStorageCombo.reason}`);
|
|
3649
3735
|
}
|
|
@@ -3703,6 +3789,8 @@ export async function cmdDeployCodeBody(flags, emit) {
|
|
|
3703
3789
|
contractURIRenderer: foldIntoInit ? metadataRenderer : zeroAddress,
|
|
3704
3790
|
royaltyReceiver: owner,
|
|
3705
3791
|
royaltyBps,
|
|
3792
|
+
maxRoyaltyBps,
|
|
3793
|
+
burnable,
|
|
3706
3794
|
transferValidator,
|
|
3707
3795
|
maxInvocations: BigInt(max),
|
|
3708
3796
|
primaryPayee: flags['primary-payee'] ?? zeroAddress,
|
|
@@ -4060,7 +4148,7 @@ export async function cmdDeployCodeBody(flags, emit) {
|
|
|
4060
4148
|
export const DEPLOY_CODE_EDITION_FLAGS = new Set([
|
|
4061
4149
|
'script', 'name', 'symbol', 'description', 'external-url', 'description-onchain',
|
|
4062
4150
|
...AUTHORSHIP_DEPLOY_FIELDS.map(([flag]) => flag),
|
|
4063
|
-
'max', 'mint-count', 'mint-all', 'no-mint', 'mint-amount', 'unpaused', 'minter', 'primary-payee', 'royalty-bps', '721c',
|
|
4151
|
+
'max', 'mint-count', 'mint-all', 'no-mint', 'mint-amount', 'unpaused', 'minter', 'primary-payee', 'royalty-bps', 'royalty-cap', 'burnable', '721c',
|
|
4064
4152
|
'schema', 'no-seed', 'seed-source',
|
|
4065
4153
|
'public-base-url', 'onchain-uri', 'generator', 'renderer', 'port',
|
|
4066
4154
|
'sign', 'unsigned', 'for', 'salt', 'factory', 'bootstrap-factory', 'sign-url-file',
|
|
@@ -4118,7 +4206,16 @@ export async function cmdDeployEditionCodeBody(flags, emit) {
|
|
|
4118
4206
|
throw new Error('--mint-amount 0 with ids being pre-minted at deploy makes no sense — pass a positive --mint-amount, or drop --mint-all/--mint-count to defer minting entirely.');
|
|
4119
4207
|
}
|
|
4120
4208
|
const paused = flags.unpaused === undefined;
|
|
4121
|
-
const royaltyBps =
|
|
4209
|
+
const royaltyBps = flags['royalty-bps'] === undefined ? 500 : parseRoyaltyBps(String(flags['royalty-bps']));
|
|
4210
|
+
// B45: owner-set royalty ceiling (bps, up to 100%), reduce-only after deploy. Defaults to 10%,
|
|
4211
|
+
// auto-raised to fit a higher --royalty-bps so a plausible input never errors; --royalty-cap overrides.
|
|
4212
|
+
const maxRoyaltyBps = flags['royalty-cap'] === undefined
|
|
4213
|
+
? Math.max(1000, royaltyBps)
|
|
4214
|
+
: parseRoyaltyBps(String(flags['royalty-cap']));
|
|
4215
|
+
if (royaltyBps > maxRoyaltyBps) {
|
|
4216
|
+
throw new Error(`--royalty-bps ${royaltyBps / 100}% exceeds --royalty-cap ${maxRoyaltyBps / 100}%; raise the cap or lower the royalty.`);
|
|
4217
|
+
}
|
|
4218
|
+
const burnable = flags.burnable === '' || flags.burnable === 'true';
|
|
4122
4219
|
const port = Number(flags.port ?? process.env.ABX_PORT ?? DEFAULT_PORT);
|
|
4123
4220
|
const baseUrl = (flags['public-base-url'] ?? resolveBaseUrl(port)).replace(/\/$/, '');
|
|
4124
4221
|
const hasPublicUrl = !!(flags['public-base-url'] || process.env.ABX_PUBLIC_BASE_URL);
|
|
@@ -4261,6 +4358,8 @@ export async function cmdDeployEditionCodeBody(flags, emit) {
|
|
|
4261
4358
|
contractURIRenderer: zeroAddress,
|
|
4262
4359
|
royaltyReceiver: owner,
|
|
4263
4360
|
royaltyBps,
|
|
4361
|
+
maxRoyaltyBps,
|
|
4362
|
+
burnable,
|
|
4264
4363
|
transferValidator,
|
|
4265
4364
|
maxInvocations: BigInt(max),
|
|
4266
4365
|
editionSize,
|
|
@@ -4354,7 +4453,7 @@ export async function cmdDeployEditionCodeBody(flags, emit) {
|
|
|
4354
4453
|
info(`deployer: ${dim('not set')} — pass ${bold('--for 0x..')} to preview the exact deterministic address.`);
|
|
4355
4454
|
if (shownAddr)
|
|
4356
4455
|
info(`deterministic address: ${shownAddr}`);
|
|
4357
|
-
info(`name "${name}" · symbol ${symbol} · max ${max} id(s) · royalty ${royaltyBps / 100}
|
|
4456
|
+
info(`name "${name}" · symbol ${symbol} · max ${max} id(s) · royalty ${royaltyBps / 100}% · cap ${maxRoyaltyBps / 100}%${burnable ? ' · burnable' : ''}`);
|
|
4358
4457
|
// `source` is what this lane read; there has never been a `bytes` binding here, so this line
|
|
4359
4458
|
// threw a ReferenceError for every `deploy-code --copies … --dry-run` — the dry run crashed at the
|
|
4360
4459
|
// last step of the plan it was printing. Byte length, not UTF-16 code units, matching the 721 twin.
|