@artblocks/abx-cli 0.1.0-alpha.32 → 0.1.0-alpha.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/dist/capabilities.d.ts +94 -0
  3. package/dist/capabilities.d.ts.map +1 -0
  4. package/dist/capabilities.js +135 -0
  5. package/dist/capabilities.js.map +1 -0
  6. package/dist/commands/deploy.d.ts.map +1 -1
  7. package/dist/commands/deploy.js +13 -17
  8. package/dist/commands/deploy.js.map +1 -1
  9. package/dist/commands/feedback.d.ts +7 -0
  10. package/dist/commands/feedback.d.ts.map +1 -0
  11. package/dist/commands/feedback.js +147 -0
  12. package/dist/commands/feedback.js.map +1 -0
  13. package/dist/commands/reads.js +1 -1
  14. package/dist/commands/reads.js.map +1 -1
  15. package/dist/commands/scaffold.d.ts +9 -1
  16. package/dist/commands/scaffold.d.ts.map +1 -1
  17. package/dist/commands/scaffold.js +60 -13
  18. package/dist/commands/scaffold.js.map +1 -1
  19. package/dist/commands/service.d.ts.map +1 -1
  20. package/dist/commands/service.js +5 -4
  21. package/dist/commands/service.js.map +1 -1
  22. package/dist/flag-allowlists.d.ts.map +1 -1
  23. package/dist/flag-allowlists.js +19 -0
  24. package/dist/flag-allowlists.js.map +1 -1
  25. package/dist/main.js +45 -11
  26. package/dist/main.js.map +1 -1
  27. package/dist/ownerops.d.ts +3 -3
  28. package/dist/ownerops.js +5 -5
  29. package/dist/ownerops.js.map +1 -1
  30. package/dist/remote.d.ts +5 -1
  31. package/dist/remote.d.ts.map +1 -1
  32. package/dist/remote.js +31 -2
  33. package/dist/remote.js.map +1 -1
  34. package/dist/scaffold.js +1 -1
  35. package/dist/scaffold.js.map +1 -1
  36. package/dist/update-check.d.ts +6 -1
  37. package/dist/update-check.d.ts.map +1 -1
  38. package/dist/update-check.js +40 -17
  39. package/dist/update-check.js.map +1 -1
  40. package/package.json +6 -6
  41. package/skill/SKILL.md +174 -526
  42. package/skill/agents/openai.yaml +4 -0
  43. package/skill/reference/capabilities.md +171 -285
  44. package/skill/reference/code.md +210 -0
  45. package/skill/reference/creator-token.md +90 -95
  46. package/skill/reference/deploy.md +167 -0
  47. package/skill/reference/diagnose.md +165 -0
  48. package/skill/reference/hosting.md +148 -126
  49. package/skill/reference/operate.md +181 -0
  50. package/skill/reference/services.md +76 -0
  51. package/skill/reference/setup.md +108 -62
  52. package/skill/reference/code-projects.md +0 -368
  53. package/skill/reference/decisions.md +0 -182
  54. package/skill/reference/operating.md +0 -220
  55. package/skill/reference/troubleshooting.md +0 -65
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "ABX"
3
+ short_description: "Launch, host, and operate ABX NFT projects"
4
+ default_prompt: "Use $abx to plan and safely execute this ABX project workflow."
@@ -1,301 +1,187 @@
1
- # Can ABX do that? — the four seams, and the honest no-list
1
+ # Capability questions and extension seams
2
2
 
3
- **Read this before you tell a creator something is impossible.** The most expensive failure on this
4
- toolkit is not a bad deploy — it is an agent saying "ABX can't do that" about something ABX does. Three
5
- real sessions in one week:
3
+ Use this reference before answering whether ABX can support an unusual mechanic. The goal is neither
4
+ automatic optimism nor reflexive refusal; it is a proven route with explicit cost and limits.
6
5
 
7
- - a creator asked to put a **77 KB PNG fully on-chain**; the agent called it "not practical" and sent
8
- them to a different protocol. It ships today, warns once, and costs ~31M gas to read.
9
- - a creator wanted **transfer restrictions plus mutable state**; the agent said no. The creator said
10
- "isn't that hooks?", the agent agreed and built it that afternoon.
11
- - an agent wrote a **bespoke, non-canonical token contract** for a mechanic two stock hooks cover —
12
- permanently forfeiting the factory provenance that is the whole point of deploying here.
6
+ ## Contents
13
7
 
14
- None of those agents lacked information. Each answered from the **command list**, and the command list
15
- is a set of *recommended routes*, not the edge of what the protocol does. Falling off the end of a
16
- routing table is not a "no".
8
+ - [Use the four-way classification](#use-the-four-way-classification)
9
+ - [Inspect the machine-readable contract](#inspect-the-machine-readable-contract)
10
+ - [Use canonical extension seams](#use-canonical-extension-seams)
11
+ - [Map mechanics to seams](#map-mechanics-to-seams)
12
+ - [Respect deploy-time prerequisites](#respect-deploy-time-prerequisites)
13
+ - [Know the supported boundary](#know-the-supported-boundary)
17
14
 
18
- ## The rule
15
+ ## Use the four-way classification
19
16
 
20
- **Never answer "ABX can't do that" from the absence of a flag.** Before the words *not possible*, *not
21
- practical*, *you'd need a different* — or the name of another protocol — leave your mouth:
17
+ Classify the request as one of:
22
18
 
23
- 1. **Check the seam table below.** Can a minter or one of the three hooks carry it? Almost always yes.
24
- 2. **Check [the genuine no-list](#the-genuine-no-list) at the bottom.** Is it actually on it? It is a
25
- short list, and it is the whole list.
26
- 3. **If it is on neither, it is possible.** Answer with the *cost*, not a refusal.
19
+ 1. **Native** a current CLI command/flag combination performs it directly.
20
+ 2. **Extension** a custom minter, hook, field renderer, seed source, or transfer validator performs
21
+ it while the token remains a canonical factory clone.
22
+ 3. **Unsupported/foreclosed** the capability contract lists it as unsupported, or the existing
23
+ contract's immutable type/options exclude the prerequisite.
24
+ 4. **Unknown** — no supported route has been demonstrated. Inspect help, implementation/contracts,
25
+ and state; report uncertainty if it remains unproven.
27
26
 
28
- **The answer shape is always "yes, and here is what it costs" never a flat no.** A creator who hears
29
- *"that's not practical"* hears *"ABX can't"* and leaves. A creator who hears *"that's about 31M gas to
30
- read, so it needs a high-gas RPC and most marketplaces will show nothing — worth it if permanence is the
31
- point, and `--backend arweave` is the alternative"* can actually decide. The second one is also true.
27
+ Never infer “unsupported” from the absence of a flag. Never infer “possible” merely because an item is
28
+ absent from a short no-list. A capability claim needs a native lane or an exact extension seam.
32
29
 
33
- **Never route a creator to another protocol for something ABX does.** If ABX genuinely cannot do it,
34
- say so plainly and stop recommending a competitor's stack is not your call to make on their behalf,
35
- and it has so far always been wrong on the facts.
30
+ Give the creator the route, engineering required, authority model, gas/hosting/reach tradeoff,
31
+ irreversible prerequisites, and verification plan. “Possible” without those qualifiers is not useful.
36
32
 
37
- ## The four seams
33
+ ## Inspect the machine-readable contract
38
34
 
39
- A canonical ABX token is a stock factory clone that never changes. Your mechanic lives *beside* it, in
40
- contracts the collector can also read. That separation is the product: the buyer verifies the token
41
- against `isAbxClone`, and verifies your rules independently.
35
+ Run:
42
36
 
43
- | Seam | Wired with | Runs | Can it veto? |
44
- |---|---|---|---|
45
- | **Minter** | `abx set-minter <token> --minter 0x…` (Series or edition — a plain 1/1 has no minter) | when your contract calls `mint(to)` — it may do anything first | n/a — it *is* the mint |
46
- | **Configure hook** | `abx set-param-hooks <token> --configure 0x…` | before a governed param write persists | **yes** — its revert rejects the write |
47
- | **Transfer hook** | `abx set-param-hooks <token> --transfer 0x…` | after every ownership change, **mint and burn included** | **yes** — its revert fails the transfer |
48
- | **Augment hook** | `abx set-param-hooks <token> --augment 0x…` | at read time, when `tokenData` is assembled | no — it **adds and overrides** keys |
49
-
50
- One contract can be several of these at once. That is the shape nearly every real mechanic takes: a
51
- controller holding the money and the rules, wired into a clone that stays byte-identical to every other
52
- ABX project of its type.
53
-
54
- **Between them, these seams run arbitrary Solidity at all three moments in a token's life: when someone
55
- writes to it, when it moves, and when it is read.** That is the honest scope of the claim. If a mechanic
56
- can be expressed as *"reject this write"*, *"reject this move"*, *"account for what just happened"*, or
57
- *"compute this at read time"*, it is buildable here without a custom token.
58
-
59
- > **⚠ The precondition, and it is deploy-time-irreversible: hooks exist only on `SeriesCode` /
60
- > `EditionCode` — the `deploy-code` lane.** A `deploy` 1/1 or a `deploy-series` image collection has no
61
- > configurable params, therefore no hooks, and `set-param-hooks` refuses it. **The contract type is fixed
62
- > at deploy and cannot be retrofitted**, so a static-art collection deployed with `deploy-series` has
63
- > permanently foreclosed every mechanic on this page. **If a creator has hinted at *any* rule — "holders
64
- > can rename it", "it can't be resold for a month", "it levels up", "it reacts to the chain" — deploy it
65
- > with `deploy-code` even when the art is a plain image** (a script that draws the image is fine, and
66
- > `--image-renderer` covers the Solidity lane). Ask before you deploy; you cannot ask after.
67
- >
68
- > **The minter seam is the exception** — `abx set-minter` works on any Series or edition, so an
69
- > auction, an allowlist or a raffle needs no code project. Only the three hooks do.
70
-
71
- There is a **fifth** transfer-time seam that is not a hook and should never be conflated with one:
72
- the **ERC-721C / ERC-1155C transfer validator**, an opt-in-at-deploy standards-track mechanism for
73
- royalty enforcement, managed with `abx set-transfer-validator`. Different purpose, different lock,
74
- different audience → [creator-token.md](creator-token.md).
75
-
76
- ## The three hooks, in depth
77
-
78
- Full calling conventions live in
79
- [`IAbxParamHooks.sol`](https://github.com/ArtBlocks/abx/blob/main/contracts/src/extensions/configurable-params/IAbxParamHooks.sol);
80
- working Solidity for each pattern is at
81
- https://abx.docs.artblocks.io/using-abx/guides/custom-mechanics/. Wire all three with one
82
- `abx set-param-hooks` call (the contract has no per-hook setter — omit a role to keep it, `none` to
83
- clear it).
84
-
85
- ### Configure hook — the write-time validator
86
-
87
- ```solidity
88
- function onParamConfigured(
89
- uint256 tokenId, bytes32 key, bytes32 value,
90
- address updatedBy, uint256 dataLength, address dataBlobAddress
91
- ) external;
37
+ ```bash
38
+ abx capabilities --json
39
+ abx help <relevant-command>
92
40
  ```
93
41
 
94
- Called **before the value persists**, inside a schema-governed `configure-param`. **Reverting vetoes the
95
- write.**
96
-
97
- - **`tokenParam` still returns the OLD value during this call** — which is what makes "may only go up",
98
- "may only be set once", and "must differ from the last" expressible at all.
99
- - **Two shapes, one function.** A scalar write puts the whole value in `value` and zeroes the last two
100
- args. A `String`/`Bytes` write cannot — `value` carries only `keccak256(data)`, and the content is at
101
- `dataBlobAddress` (a Solady SSTORE2 pointer, already deployed). `dataLength` is the reliable
102
- discriminator: **never `0` on the blob path**.
103
- - Read the incoming blob with `SSTORE2.read(dataBlobAddress)` — never `CALL` the pointer. A hook that
104
- only enforces a size ceiling reads `dataLength` and never pays to touch the bytes.
105
- - **A veto costs the writer the storage they already paid for** (the blob is written before the hook
106
- runs, by necessity). An `eth_call` dry run surfaces the rejection for free — that is what a good
107
- configuration UI does before asking for a signature.
108
- - **You cannot write zero bytes.** A zero-length `String`/`Bytes` write is refused by the contract
109
- before your hook ever runs, so "unequip everything" cannot be an empty payload — use a sentinel byte
110
- or a scalar companion key.
111
-
112
- ### Transfer hook — the ownership-change veto
113
-
114
- ```solidity
115
- function onTokenTransfer(
116
- uint256 tokenId, address from, address to, address operator, uint256 amount
117
- ) external;
118
- ```
42
+ The capability contract reports native deployment lanes, EditionCode/static edition differences,
43
+ extension seams, deploy-time choices, and currently unsupported product boundaries. For an existing
44
+ collection, also run `abx state`; capability depends on the contract already deployed, not only on
45
+ what the newest CLI could deploy today.
119
46
 
120
- Called after every ownership change. **`from == address(0)` is a mint; `to == address(0)` is a burn.**
121
- **Reverting fails the transfer** the token no longer swallows the revert, because it could not honestly
122
- promise otherwise (Solady runs the receiver-acceptance check *after* this call, so a hook could starve
123
- `safeTransferFrom` regardless).
124
-
125
- - **It is a veto, not a trigger.** It can refuse a move; it can never *cause* one.
126
- - **It vetoes mints too** — a mint is a transfer from `0x0`, so a hook that reverts unconditionally
127
- freezes issuance as well as trading.
128
- - **It sees burns**, which is how burn-to-combine, redemption and vaults settle — but only on a
129
- collection deployed `--burnable`, which is **deploy-time-only and cannot be retrofitted**.
130
- - It may persist and derive state through its own authority; the token exposes no special path.
131
- - `operator` and `amount` are not redundant with `from`: on an ERC-1155 an approved marketplace moves a
132
- holder's copies. **On a multi-copy edition, think hard before storing per-id state** — params are per
133
- *id* and therefore shared by every holder, so "the current owner" really means "whoever moved most
134
- recently". Aggregate or monotonic state (transfer counts, "has ever been held by") is coherent there;
135
- a single-owner notion is not.
136
- - **A buyer's guarantee that you will never arm one is `abx lock-param-hooks`, not your word.** Freezing
137
- an empty set is the proof that no transfer veto can ever exist.
138
-
139
- ### Augment hook — read-time compute, and the most under-used seam we have
140
-
141
- ```solidity
142
- function augmentTokenParams(address token, uint256 tokenId)
143
- external view returns (AugmentedParam[] memory); // { bytes32 key; string value; }
144
- ```
47
+ Use a deploy command's `--dry-run --json` for a concrete plan. It is the source of truth for the
48
+ active chain, artifacts, signer, storage, addresses, transaction count, and measured warnings.
145
49
 
146
- A `view`, called when `tokenData` is assembled — by the on-chain `AbxGenerator` and by the off-chain
147
- resolver. **No storage, no event, no gas on the token.** It may **add** keys and **override** stored ones
148
- (augment wins per key, last-wins in every mainstream JSON parser).
149
-
150
- - **Its return is bounded only by `eth_call` limits the ~24 KB stored-blob ceiling does not apply to
151
- compute.** This is the lane for large *derived* data.
152
- - It reads whatever a `view` can reach: block data, an oracle, another contract, the token's own params,
153
- a holder's balance elsewhere. A piece that genuinely reacts to live chain state is one hook.
154
- - Values are the **final canonical string** exactly as `tokenData` carries it a hook injecting binary
155
- base64s it itself (free, view context). Raw, not JSON-escaped; escaping is the serializer's job.
156
- - **The live view reads it per view (always current); the rendered still is a snapshot of settled state
157
- only.** So a volatile hook (a timestamp, a price) animates the live view without re-rendering the
158
- thumbnail every block. Re-render triggers are settled-state changes.
159
- - Reserved `tokenData` keys (`seed`, `tokenId`, …) are rejected — a hook cannot forge them.
160
- - **It does not reach a renderer you wrote.** `render(token, tokenId, field)` takes no `tokenData`.
161
- Whoever builds `tokenData` calls the hook, and in your own renderer that is you: read the live state
162
- directly, or read `paramHooks()` and call `augmentTokenParams` yourself, wrapped so a broken hook
163
- cannot make `render()` revert. Do **not** tell a creator "augment hooks are a resolver concept"
164
- the generator calls one on-chain.
165
- - Resolver operators have a kill-switch (`ABX_DISABLE_AUGMENT=1`, degrading to settled params);
166
- `abx verify` prints the project's live-data posture.
167
-
168
- ## Mechanic seam (the inverse lookup)
169
-
170
- Every one of these has been built here, or is one small contract away. When a creator describes a
171
- mechanic, find the row before you find an objection.
172
-
173
- | The creator wants | Seam | Shape |
174
- |---|---|---|
175
- | Can't be resold for N days · vesting · lockup | transfer hook | `require(block.timestamp >= unlockAt)` |
176
- | Soulbound — mint once, never moves | transfer hook | `require(from == address(0))` |
177
- | Burn to redeem · burn to combine · breeding | `--burnable` + transfer hook | settle on `to == address(0)` |
178
- | Escrow / vault — ETH locked behind the token | minter **+** transfer hook | take payment on mint, pay out on burn |
179
- | A value that may only go up (level, growth, high-water) | configure hook | compare against the still-old `tokenParam` |
180
- | Structured collector input that must be well-formed (equip, layout, inventory) | configure hook | `SSTORE2.read(dataBlobAddress)`, decode, validate |
181
- | Only holders of *some other* token may configure this | configure hook, or `Address(…)` auth in the schema | check the other contract in the hook |
182
- | Auction · Dutch auction · allowlist · raffle · free claim | **custom minter** | your contract, `abx set-minter` |
183
- | Priced in an ERC-20, or in anything but ETH | **custom minter** | take payment however you like, then `mint(to)` |
184
- | Art that reacts to live chain state (block, price, an oracle) | augment hook | read it in a `view`, return it as `tokenData` |
185
- | Timed reveal | augment hook | branch on `block.timestamp` |
186
- | Derived traits too large to store on-chain | augment hook | compute at read time — no blob ceiling |
187
- | Provenance / transfer count / "has ever been held by" | transfer hook | the hook writes its own storage |
188
- | A raster of any size fully on-chain | `--onchain-image` | nothing is refused [below](#on-chain-size-nothing-is-refused-and-the-question-is-who-can-read-it) |
189
- | Royalty enforcement at the marketplace layer | ERC-721C validator (opt in **at deploy**) | [creator-token.md](creator-token.md) |
190
-
191
- **The controller chicken-and-egg has a solution — use it.** The controller needs the token's address at
192
- construction and the token's schema needs the controller's address. Do not run the five-step dance:
193
- `abx predict` computes the deterministic address first, so the controller is built against a constant and
194
- the schema rides the deploy. Recipe, including the salt guard-prefix trap (leading 20 bytes must equal
195
- your deploying wallet, or the address is permissionless and anyone may take it):
196
- https://abx.docs.artblocks.io/using-abx/guides/custom-mechanics/#break-the-controller-cycle-with-abx-predict
197
-
198
- **`abx scaffold-renderer` is the fastest start even when you are building a hook, not a renderer** it
199
- writes a Foundry project with the interfaces vendored and tests wired. And test against a *real* deployed
200
- clone (`forge test --fork-url`), never a hand-rolled mock: a mock invents its way around the real edge
201
- cases, starting with the zero-length refusal above.
202
-
203
- ## On-chain size: nothing is refused, and the question is who can READ it
204
-
205
- The routing tables elsewhere in this skill recommend **≲40 KB/token** for on-chain content. That is
206
- **advice about what is cheap, not a limit** — and reading it as a limit is what produced the 77 KB
207
- refusal. There is no size the toolkit refuses.
208
-
209
- **Two axes, and only one is about size at all.** The **write** is chunked into 22,000-byte SSTORE2
210
- transactions, so no block gas limit binds it at any size — purely money, ~200 gas/byte, paid once. The
211
- **read** is a single `eth_call`, and whether it succeeds is a property of *whose endpoint is asking*.
212
-
213
- Measured 2026-08-24, which is why the old 100 KB refusal was wrong:
214
-
215
- | Endpoint | `eth_call` cap | Content it serves |
50
+ ## Use canonical extension seams
51
+
52
+ A custom mechanic normally lives beside a canonical token rather than replacing it.
53
+
54
+ | Seam | Wired with | Runs | Can veto? |
55
+ |---|---|---|---|
56
+ | Custom minter | `abx set-minter --minter 0x…` | before/calling issuance | it controls whether it calls mint |
57
+ | Configure hook | `abx set-param-hooks --configure 0x…` | before a governed parameter write persists | yes |
58
+ | Transfer hook | `abx set-param-hooks --transfer 0x…` | on mint, transfer, and burn | yes |
59
+ | Augment hook | `abx set-param-hooks --augment 0x…` | while token data is assembled | no; computes/adds/overrides data |
60
+ | Image/traits renderer | deploy-code renderer flags | while on-chain metadata fields render | a revert can break that read; design never-revert |
61
+ | Seed source | `abx set-seed-source` | future code-token mints | supplies mint-time seed input |
62
+ | Transfer validator | `--721c` then `set-transfer-validator` | standards-track transfer validation | yes, according to validator |
63
+
64
+ One external contract may implement several hook/minter roles. Test it against a real deployed clone,
65
+ not a mock that approximates ABX callbacks.
66
+
67
+ ABX scaffolds a Foundry renderer project but does not compile or deploy custom Solidity. The creator
68
+ or their contract engineer owns code review, deployment, verification, upgrade policy, and audits.
69
+
70
+ ### Custom minter
71
+
72
+ Series and editions can authorize one minter contract. Use it for pricing/allocation rules that do not
73
+ fit the shared fixed-price minter: auctions, allowlists, raffles, free claims, ERC-20 payment, escrowed
74
+ issuance, or other pre-mint logic. The minter eventually calls the token's canonical mint entry point.
75
+
76
+ A plain 1/1 has no Series minter seam. Decide whether the requested issuance model actually requires
77
+ a Series/edition before deploying.
78
+
79
+ ### Configure hook
80
+
81
+ The configure hook receives the proposed typed parameter write before it persists. Its revert rejects
82
+ the write. It can enforce monotonicity, one-time writes, cross-contract authorization, payload shape,
83
+ or size bounds. For blob-backed strings/bytes, validate length before reading large data and read the
84
+ provided data pointer using the documented SSTORE2 convention.
85
+
86
+ The previously stored value remains visible during validation, enabling comparisons. A rejected
87
+ write may still have paid for preparatory blob storage; front ends should simulate before asking for a
88
+ signature.
89
+
90
+ ### Transfer hook
91
+
92
+ The transfer hook runs for mints (`from == 0`), ordinary transfers, and burns (`to == 0`). Reverting
93
+ vetoes the entire operation. It can implement vesting, soulbinding, redemption, escrow settlement,
94
+ provenance counters, and lifecycle rules.
95
+
96
+ It cannot initiate a transfer; it only observes or rejects one. Burn-based mechanics require the
97
+ collection to have been deployed burnable. On editions, ids may have many holders, so shared per-id
98
+ state cannot model a single current owner.
99
+
100
+ ### Augment hook
101
+
102
+ The augment hook is a read-time view seam. It may read other contracts, an oracle, block state, or
103
+ stored params and return final token-data strings. Use it for live/derived state or computed data that
104
+ should not be stored.
105
+
106
+ Volatile augmentation makes the live view change without an event or stored write. A marketplace still
107
+ remains a snapshot until it is rendered again. If a custom Solidity field renderer needs augmented
108
+ state, it must read/call the relevant hook/state itself; field-renderer calls do not receive a prepared
109
+ token-data object.
110
+
111
+ ## Map mechanics to seams
112
+
113
+ | Request | Primary route | Prerequisites/limits |
216
114
  |---|---|---|
217
- | **`sepolia.base.org`** the default endpoint of our default chain | **600M** | **~729 KB** |
218
- | `base-sepolia.publicnode.com` · `base-sepolia.drpc.org` | 50M | ~117 KB |
219
- | `ethereum-sepolia.publicnode.com` | 50M | ~117 KB |
220
-
221
- 50M is the **floor**, not the ceiling. The refusal sat at 100 KB below every endpoint above — so it
222
- blocked content all of them could read. The CLI now measures the RPC you are actually on (a
223
- state-override call that sends nothing) and reports it:
224
-
225
- | Content per token | What the CLI does |
226
- |---|---|
227
- | **117 KB** (~50M gas) | a plain note with write and read figures — renders anywhere |
228
- | **above that** | names your RPC's measured cap and warns that a 50M-capped provider shows a **revert** |
229
- | **past your own RPC's cap** | says plainly that nothing you can point at renders it |
230
-
231
- For that **77 KB PNG**: **~16M gas to write** (four chunks) and **~31M to read** — under the floor, so
232
- it renders everywhere. Say the numbers; it is not a warn case at all.
233
-
234
- **Two things have to survive into the conversation.**
235
-
236
- **(1) We can measure YOUR RPC. We cannot know a marketplace's or an indexer's** and theirs decide
237
- whether the token displays. A measured cap proves *you* can read it, never that OpenSea can, and it is
238
- point-in-time besides (pooled endpoints rotate: Sepolia's publicnode measured 50M one hour and 2,000M
239
- the next). Say the measurement as a measurement.
240
-
241
- **(2) Reach is fixable after the fact; the bytes are permanent either way.** The read cost decides
242
- whether a token resolves *in one `eth_call`*, not whether the content is reachable. A resolver
243
- (`abx deploy-resolver`, or a hosted one) reads on-chain content with **its own** RPC — `inline`,
244
- `inline-gzip`, `reader`, `reader-gzip` all resolve through it and serves plain HTTP, so a marketplace
245
- fetches a URL and never makes the big call. `abx set-renderer <addr> --off` repoints `tokenURI` at it
246
- with every byte left exactly where it is.
247
-
248
- So keep **storage** and **serving** separate, because they are: on-chain is the strongest storage
249
- answer available at *any* size, and self-resolving-vs-resolver is a serving decision the creator can
250
- change later. A token whose read is too big for some endpoint is not a lost token — it is one that
251
- wants a reader in front of it. `--backend arweave` is the other side of the *storage* trade, not a
252
- correction of their ask.
253
-
254
- **Never quote a BLOCK gas limit as the read ceiling.** Three limits share the name and differ by orders
255
- of magnitude on one endpoint (measured 2026-08-24, Base Sepolia): **`eth_call` 600M** bounds the read
256
- that decides display · **`eth_estimateGas` 16,777,216** bounds what can be *sent* (identical on every
257
- endpoint tried it is why writes batch at 8M, not the block limit) · **block `gasLimit` 1,200M**
258
- bounds a contract reading inside a transaction, so a contract there reads ~1,100 KB. Off-chain reads
259
- are not bounded by block limits, and are routinely far above them.
260
-
261
- Two things not to garble: `--compress fastlz` cuts the **write** cost, never the read (the renderer
262
- rebuilds the full uncompressed bytes every call). And the gate is **per token** — a 300-piece collection
263
- of 5 KB works reads perfectly well.
264
-
265
- Measured gas table and the per-KB curve →
266
- [decisions.md The READ cost](decisions.md#the-read-cost--who-can-see-it-and-why-nothing-is-refused).
267
-
268
- ## The genuine no-list
269
-
270
- This is the whole list. If the ask is not here and not blocked by a deploy-time choice already made,
271
- stop looking for a reason to say no.
272
-
273
- **Actually not possible today:**
274
-
275
- - **Mainnet.** Testnet only Base Sepolia by default, Sepolia via `ABX_CHAIN`. Never imply otherwise.
276
- - **Secondary listings.** Reselling a held token means an external marketplace or a manual
277
- `abx transfer`. There is no listing feature and no order book.
278
- - **`abx` compiling or deploying Solidity for you.** You write hooks, minters and renderers with Foundry
279
- and deploy them yourself; `abx scaffold-renderer` writes the project, it does not build or ship it.
280
- - **Replacing a code project's script through the CLI post-deploy.** There is no `abx set-script`.
281
- - **Retrofitting a deploy-time choice.** These are permanent from the moment the deploy lands:
282
- the **contract type** (hooks require `deploy-code`), **`--burnable`**, **`--copies`** (721 vs 1155),
283
- and **ERC-721C enrollment**. Ask about all four *before* the deploy — this is the single most common
284
- way a creator ends up genuinely unable to have what they wanted.
285
- - **Unsupported chains.** Say plainly it is not in the toolkit; do not fabricate a recipe.
286
-
287
- **Not a flag — but absolutely possible on a seam.** These belong in the paragraph above only if you also
288
- say how they are built. Listing them as things "abx doesn't do" is what caused this file to exist:
289
-
290
- - auctions, Dutch auctions, allowlists, raffles, free claims, ERC-20 pricing → **a custom minter**
291
- - transfer restrictions, soulbinding, vesting, redemption, escrow → **the transfer hook**
292
- - validated or governed collector input → **the configure hook**
293
- - live, derived, oracle-fed or oversized metadata → **the augment hook**
294
- - a large raster fully on-chain → **`--onchain-image`**, at any size
295
-
296
- **And one thing that is possible but is almost never the right answer: writing your own token contract.**
297
- It is a legitimate creator choice, but it permanently forfeits `isAbxClone` — the only signal
298
- marketplaces and the App Store allowlist against — and canonicity cannot be added afterwards. Never make
299
- that trade *for* a creator silently. Say: *"I can do that, but your collection won't be a canonical ABX
300
- contract — marketplaces won't recognise it, and the only way back is redeploying and moving holders.
301
- Want that, or shall we find a way inside the toolkit?"* Then wait for an answer.
115
+ | Auction, allowlist, raffle, free claim | custom minter | Series or edition mint authority |
116
+ | ERC-20 priced mint | custom minter | payment logic/audit outside ABX |
117
+ | Soulbound or timed transfer lock | transfer hook | deploy-code; hook also sees mint |
118
+ | Burn-to-redeem/combine | external controller/minter + burnable; optional transfer/augment hooks | ABX provides seams, not the atomic combine implementation |
119
+ | Escrow tied to token lifecycle | minter + transfer hook | external value-holding contract |
120
+ | Monotonic level/high-water mark | governed param + configure hook | schema and old-value comparison |
121
+ | Structured collector configuration | governed param + configure hook | typed payload and bounded size |
122
+ | Holder of another token may configure | schema address auth or configure hook | cross-contract read |
123
+ | Art reacts to oracle/block/other contract | augment hook or renderer direct read | disclose live/non-deterministic behavior |
124
+ | Timed reveal | augment hook or renderer logic | live view versus still refresh distinction |
125
+ | On-chain SVG and coherent traits | image + attributes renderers | never-revert Solidity implementations |
126
+ | Large raster stored on-chain | `--onchain-image` | measured write cost and endpoint-dependent read reach |
127
+ | Royalty-aware transfer validation | ERC-721C/ERC-1155C validator | opt-in at deploy; marketplace compatibility varies |
128
+
129
+ When several routes work, prefer the smallest authority surface. Use the stock fixed-price minter
130
+ instead of custom Solidity for an ordinary sale; use a schema without a hook when static authorization
131
+ is sufficient; use an on-chain renderer only when its durability/reach/cost trade is desired.
132
+
133
+ A transfer hook only observes or vetoes a burn; it does not initiate two burns or mint a replacement.
134
+ For combine mechanics, prove the controller's burn authorization and canonical mint call against a
135
+ real clone, make the transition atomic and replay-safe, and define pair/history storage. On ERC-721,
136
+ `--max` is a lifetime id cap and burns do not reopen slots, so reserve replacement-id headroom or use
137
+ a supply policy that does not require new ids. On a burnable edition, a burn frees live per-id supply,
138
+ but every copy of an id shares its id-level seed and PostParams.
139
+
140
+ ## Respect deploy-time prerequisites
141
+
142
+ Ask about mechanics before deployment. These decisions cannot be retrofitted:
143
+
144
+ - static versus code-capable contract type; hooks/PostParams require SeriesCode or EditionCode;
145
+ - ERC-721 versus ERC-1155 edition shape through `--copies`;
146
+ - burnability;
147
+ - ERC-721C/ERC-1155C enrollment.
148
+
149
+ Other choices may be mutable but become irreversible when locked: URI configuration, fields, script,
150
+ dependencies, hooks, governed values/schema, royalty ceiling, and owner authority. An existing project
151
+ may therefore be capable in protocol terms but foreclosed by its current type or locks. Say that
152
+ specifically instead of saying ABX as a whole cannot do it.
153
+
154
+ Use `abx predict` to break address cycles where an external controller needs the token address at its
155
+ own construction and the token must reference the controller during deployment. Confirm the guarded
156
+ salt/deployer and predicted address before deploying either side.
157
+
158
+ ## Know the supported boundary
159
+
160
+ The toolkit currently does not provide:
161
+
162
+ - mainnet deployment or unsupported-chain recipes;
163
+ - a secondary-market listing/order-book feature;
164
+ - Solidity compilation/deployment through `abx`;
165
+ - CLI replacement of a deployed code project's script;
166
+ - retrofitting a deploy-time contract type, burnability, token standard, or creator-token enrollment.
167
+
168
+ Do not recommend another protocol automatically when one of these applies. State the boundary and
169
+ stop unless the creator asks for alternatives.
170
+
171
+ Also distinguish unsupported tooling from possible external engineering. For example, ABX does not
172
+ compile a custom hook, but a hook is an intentional supported seam once independently built and
173
+ deployed. Conversely, “arbitrary Solidity exists” is not proof that every behavior is safe or
174
+ compatible: analyze callback timing, authority, reverts, reentrancy, shared edition state, gas, and
175
+ upgradeability.
176
+
177
+ Before finalizing a custom mechanic, document:
178
+
179
+ 1. canonical token family and deploy-time prerequisites;
180
+ 2. external contracts and their roles/owners/upgrade paths;
181
+ 3. callback inputs, state transitions, and every veto path;
182
+ 4. mint/transfer/burn behavior including editions;
183
+ 5. parameter schema and authorization;
184
+ 6. live versus settled/rendered state;
185
+ 7. locks and the exact guarantee they create;
186
+ 8. unit, fork, and end-to-end test plan;
187
+ 9. collector-facing disclosure of remaining powers.