@artblocks/abx-cli 0.1.0-alpha.26 → 0.1.0-alpha.27
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 +111 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +33 -12
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/project.d.ts.map +1 -1
- package/dist/commands/project.js +33 -2
- package/dist/commands/project.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/main.js +7 -6
- package/dist/main.js.map +1 -1
- package/dist/ownerops.d.ts +2 -1
- package/dist/ownerops.d.ts.map +1 -1
- package/dist/ownerops.js +8 -6
- package/dist/ownerops.js.map +1 -1
- package/package.json +6 -6
- package/skill/SKILL.md +265 -171
- package/skill/reference/decisions.md +43 -2
- package/skill/reference/operating.md +34 -1
- package/skill/reference/setup.md +42 -1
package/skill/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: abx-self-host
|
|
|
3
3
|
description: Launch and operate a self-hosted ABX NFT end to end with the ABX CLI (`abx`) on testnet — a 1/1 (`abx deploy`), a multi-token Series from a folder of media (`abx deploy-series`), or a generative/code drop (`abx deploy-code`). Covers on-chain vs off-chain metadata, storage custody (local disk, S3/R2, IPFS, Arweave), deploy + mint (now or pre-warmed at a predicted address), rendered thumbnails and on-chain traits for code projects, collector-configurable on-chain parameters (PostParams — typed, auth-gated, settable by creator/token-owner/address), primary sales via the shared fixed-price minter, owner ops (transfer, refresh, re-point URIs, royalties, pause/unpause, supply cap, delegate minting, and the one-way locks: fields, URI config, script, dependencies, param hooks), and optionally listing a deployed collection in the ABX App Store (`abx submit-app`, never folded into deploy). Use when the user wants to self-host an ABX project, take an image to an NFT on testnet, deploy a collection from a folder of images, launch generative/code projects (art, collectibles, game assets, or anything else), add collector-settable parameters/traits, mint or run a primary sale, refresh a listing, lock down what a project stores or freeze its param hooks, operate a project they launched, list an ABX app in the App Store, choose a storage backend, stand up hosting they own, or point a project at a hosted/managed metadata provider with an API key.
|
|
4
4
|
compatibility: Drives the abx CLI (@artblocks/abx-cli). Co-versioned with it — install/refresh with `abx skill install` so this skill matches the CLI's `abx version`. Requires Node 22.5+.
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.1.0-alpha.
|
|
6
|
+
version: "0.1.0-alpha.27"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# ABX Self-Host Toolkit (`abx`)
|
|
@@ -20,18 +20,57 @@ L3 agentic surface: image → live self-hosted NFT the creator owns — a **1/1*
|
|
|
20
20
|
|
|
21
21
|
## Read first (every session)
|
|
22
22
|
|
|
23
|
-
- **YOU run the `abx` commands — never tell the creator to run one.** You have a shell
|
|
23
|
+
- **YOU run the `abx` commands — never tell the creator to run one.** You have a shell. The creator's
|
|
24
|
+
only hands-on steps: approving in their **browser wallet** (`--sign`), giving you a value you asked
|
|
25
|
+
for, and **looking at the work in `abx preview`** (you run it; the URL is theirs). Post-deploy, run the
|
|
26
|
+
read-only next steps and *offer* the actions. Exceptions: an interactive command in their environment
|
|
27
|
+
(an OS login), and the Solidity lane's Foundry step — `abx` never compiles or deploys Solidity.
|
|
24
28
|
- **Version drift — `abx doctor`'s "Version & provenance" block catches it: binary source (npx is a ✗ — it can silently keep serving a stale cached copy), npm currency, and skill↔CLI match, each with its own fix command.** Run it before any deploy and act on a ✗ yourself (`abx skill install` / `npm i -g @artblocks/abx-cli@latest`) — never just relay the notice: a stale CLI can hold canonical addresses that have since moved, so it deploys against dead singletons.
|
|
25
29
|
- **`abx doctor` first, always** — full preflight (Node, pnpm, RPC, signing key, storage). Fix any ✗ before deploying ([Setup](#setup--environment)). A missing public-base-url is not a "set up IPFS" signal: for tiny content go on-chain, for larger content pick an off-chain backend — see [Quick start](#quick-start).
|
|
26
30
|
- **Never collect secrets in chat, and never `cat`/`grep` `.env`.** Keys, `PINATA_JWT`, S3 secrets, provider API keys → the project's `.env`. The Arweave/Turbo key is a CLI-managed file (`.abx-self-host/arweave-key.json`) — never paste it. Name the var/file; never take the value. **To see what's configured, ask the tool, not the file:** `abx doctor` and `abx remote` report each credential as set/unset without ever printing one. Reading `.env` spills every secret in it into the transcript — irreversible, and a plain `abx doctor` tells you the same thing.
|
|
27
31
|
- **Never quote a third party's price or free tier.** Hosting plans, pinning plans and gateway tiers change without telling us, and a creator makes a real decision on the number you say. Name the provider and send them to its own pricing page; if you must compare, compare *shapes* — pay-once vs. monthly, "lapses if you stop paying" vs. "nothing to renew" — which is the part that stays true. The one figure that is ours to state is Turbo's **under 100 KB free** tier, because the CLI enforces it and prints it.
|
|
28
|
-
- **Testnet only today** —
|
|
29
|
-
|
|
32
|
+
- **Testnet only today** — **Base Sepolia by default**, **Sepolia** via `ABX_CHAIN=sepolia`. Say
|
|
33
|
+
"testnet"; never imply mainnet. Testnet **is** the preview environment: it runs the real wiring, so a
|
|
34
|
+
creator should deploy there, inspect the actual result, and only then consider mainnet. One
|
|
35
|
+
cross-chain gotcha: **on-chain library deps (`--dep p5@…`) need the Art Blocks dependency registry,
|
|
36
|
+
which exists on Sepolia and NOT Base Sepolia** — a no-dependency script goes on-chain on either.
|
|
37
|
+
- **Scope = two standards.** **ERC-721** (a 1/1, a Series, or a code drop — each token unique) and
|
|
38
|
+
**ERC-1155 editions** (copies of one work), reached with **`--copies <n|open>`** on those same three
|
|
39
|
+
deploy commands. There is **no `--chain` flag** (use `ABX_CHAIN`) and **no `--erc1155`/`--standard`
|
|
40
|
+
flag** — don't invent one. Map the ask to the flag, never the standard name: "N copies" / "an open
|
|
41
|
+
edition" → `--copies`; "N unique pieces" → a plain Series. If a creator needs an unsupported chain,
|
|
42
|
+
say plainly it isn't in the toolkit rather than fabricating a recipe.
|
|
30
43
|
- **Is the work finished yet?** If the creator is still *making* the piece, you're in **[Phase 0](#phase-0--make-the-work-first-skip-every-gate-below-until-its-good)** — iterate on the work and keep every deploy question off the table until they say ship. The gates below apply to launching something that already exists.
|
|
31
44
|
- **Unfilled placeholders are not a brief.** If their prompt still contains `⟨your idea⟩`, `⟨your idea here⟩`, or any unfilled template, STOP. Ask what they actually want to make. Do **not** treat the placeholder as a creative brief, and do **not** run `abx demo` or send a transaction unless they explicitly asked for a demo.
|
|
45
|
+
- **STOP if you are about to write or deploy a contract yourself.** Everything here is a clone of an
|
|
46
|
+
ownerless **trust anchor**, and `isAbxClone` against it is the ONLY signal marketplaces and the App
|
|
47
|
+
Store allowlist against. A contract you write — or `--bootstrap-factory` on a public chain — can never
|
|
48
|
+
gain it: **canonicity is decided by which contract deployed it and cannot be added afterwards.** So if
|
|
49
|
+
the ask seems to need something the deploy commands don't do, do NOT build around it. Say: *"I can do
|
|
50
|
+
that, but your collection won't be a canonical ABX contract — marketplaces won't recognise it, and the
|
|
51
|
+
only way back is redeploying and moving holders. Want that, or shall we find a way inside the
|
|
52
|
+
toolkit?"* Then wait. Building your own contracts is a legitimate creator choice; making it **for**
|
|
53
|
+
them silently is not. (`abx state <addr>` prints `canonical` first.)
|
|
54
|
+
- **What this toolkit does NOT do** — check here before inventing a way. **No burn**: not one of the six
|
|
55
|
+
token types exposes a burn entrypoint, and transfer-to-`0x0` reverts, so **a minted ABX token cannot be
|
|
56
|
+
destroyed by anyone, including its owner** — burn-to-combine, breeding and redemption are not buildable
|
|
57
|
+
on these contracts. Also: no secondary listings · no mainnet · fixed-price sales only (auctions and
|
|
58
|
+
allowlists would be different *minters*, not flags) · no post-deploy script replace · and `abx` never
|
|
59
|
+
compiles or deploys a token contract (`scaffold-renderer` writes a renderer, not a collection). If the
|
|
60
|
+
ask needs one of these, say so plainly instead of building around it.
|
|
61
|
+
- **Two extension points keep a custom mechanic canonical — reach for these first.** A collection can
|
|
62
|
+
point at **your own minter** (`abx set-minter`), and a code project can arm **param hooks**; both keep
|
|
63
|
+
it a factory clone. Two caveats: neither is scaffolded (real engineering), and a **`--transfer` hook is
|
|
64
|
+
a VETO, not a trigger** — it can refuse a transfer but never *cause* a mint or burn, and a reverting
|
|
65
|
+
hook stops transfers **and mints** for every token. So a hook cannot implement "combine two into one".
|
|
32
66
|
- **Two gates decide everything: (1) demo or real? (2) who signs?** Settle both first — *once there's something to launch*.
|
|
33
67
|
- **Confirm the full config before any on-chain write** ([readout](#confirm-before-sending)); wait for go-ahead. Never invent a field silently (name/symbol from filename, an auto description) — show it, flag it `inferred`.
|
|
34
|
-
- **Never hand-build a service URL — ask the chain
|
|
68
|
+
- **Never hand-build a service URL — ask the chain.** `abx tokenuri <addr> --fetch` and
|
|
69
|
+
`abx contracturi <addr>` follow the URL the contract itself commits and print what is actually served.
|
|
70
|
+
**A 404 on a URL you constructed is evidence about your URL, never about the service.** Don't infer a
|
|
71
|
+
path from a similar one (`/t/<chain>/<addr>/<id>` minus the id is not collection metadata — that's
|
|
72
|
+
`/c/<chain>/<addr>`). Before calling a service broken, reproduce with a CLI command: a real miss returns
|
|
73
|
+
`invalid_request` · `unknown_route` · `not_registered`, none of which mean "down".
|
|
35
74
|
- **Safe to explore:** `abx <cmd> --help` and `abx deploy --dry-run` never send. You never run a real write just to learn flags.
|
|
36
75
|
- **`deploy` returns; `demo`/`serve`/`preview` block** (they serve) — background them or warn. Background `abx preview` and relay its URL, then keep working while the creator looks; `--shoot` is the one preview mode that exits on its own.
|
|
37
76
|
|
|
@@ -43,10 +82,11 @@ Route by the **content** first, then apply the gates below. The three paths diff
|
|
|
43
82
|
|---|---|---|---|
|
|
44
83
|
| **one image** (a 1/1) | `abx deploy` | on-chain (tiny content) or off-chain — **no server possible** | the image itself |
|
|
45
84
|
| **a folder of images** | `abx deploy-series` | same — on-chain or off-chain, **no server possible** | each image itself |
|
|
85
|
+
| **a program** (generative / code) | `abx deploy-code` | **a resolver you run** (live seed + PostParam injection) — OR `--onchain-uri` (tokenURI on-chain; the canonical generator computes the live view) | **rendered off-chain** by the effect runner, else a placeholder |
|
|
46
86
|
|
|
47
87
|
**Any row also takes `--copies <n|open>`** — it makes that work/collection an ERC-1155 **edition** (copies, not unique tokens; `open` = an uncapped open edition, the flagship edition product). Drop it for a unique token, exactly as today. Edition owner ops (mint copies, per-id supply cap, per-id sales) → [operating.md](reference/operating.md#edition-owner-ops).
|
|
48
88
|
|
|
49
|
-
**Custody on an edition is now symmetric with the 721 side** — on-chain bytes, off-chain-image-with-on-chain-JSON, inline SVG, or a resolver all work with `--copies`, and `deploy-code --copies` takes on-chain `--dep`s. Two limits remain: **`--onchain-image` can't be signed offline** (refused on `--unsigned` everywhere — each chunk tx feeds the next; use hot or `--sign`), and **`deploy-code --copies` is `--script`-only** (no `--code-dir`, no field renderers). Full matrix, including the O(1) folder trick for a raster edition with no server: [decisions.md → `--copies` custody](reference/decisions.md
|
|
89
|
+
**Custody on an edition is now symmetric with the 721 side** — on-chain bytes, off-chain-image-with-on-chain-JSON, inline SVG, or a resolver all work with `--copies`, and `deploy-code --copies` takes on-chain `--dep`s. Two limits remain: **`--onchain-image` can't be signed offline** (refused on `--unsigned` everywhere — each chunk tx feeds the next; use hot or `--sign`), and **`deploy-code --copies` is `--script`-only** (no `--code-dir`, no field renderers). Full matrix, including the O(1) folder trick for a raster edition with no server: [decisions.md → `--copies` custody](reference/decisions.md#--copies-erc-1155-editions--custody-is-not-orthogonal-to-the-shape).
|
|
50
90
|
|
|
51
91
|
> **`--onchain-uri` puts the JSON on-chain — the IMAGE follows one of three routes, and the creator
|
|
52
92
|
> is choosing between them whether they know it or not.** An **SVG** is inlined (bytes on-chain,
|
|
@@ -57,34 +97,39 @@ Route by the **content** first, then apply the gates below. The three paths diff
|
|
|
57
97
|
> `--onchain-image --compress fastlz` (**keep it under ~40 KB/token — that's a READ-gas ceiling, not a
|
|
58
98
|
> cost one; see [Quick start](#quick-start)**). Say which one you're giving them: "no server" and
|
|
59
99
|
> "on-chain" are not the same promise.
|
|
60
|
-
| **a program** (generative / code) | `abx deploy-code` | **a resolver you run** (live seed + PostParam injection) — OR `--onchain-uri` (tokenURI on-chain; the canonical generator computes the live view) | **rendered off-chain** by the effect runner, else a placeholder |
|
|
61
100
|
|
|
62
101
|
**The dividing line is static content vs a running program.** Static content is self-resolving (the file *is* the thumbnail, nothing to keep running); **a code project's thumbnail is *rendered* off-chain, so it ALWAYS needs a public home you provide (`--image-base` bucket, or a resolver) — settle that infra fork with the creator FIRST** (details in [Code projects](#code-projects-generative--code-based-drops)). Nail the project type before the gates.
|
|
63
102
|
|
|
64
103
|
**Want collector-settable state (a palette a holder picks, a governed dial, an open/communal input)? That's PostParams — a `deploy-code` capability, and the reason to reach for it isn't only "generative."** Any piece that needs typed, on-chain, auth-gated parameters (settable by the creator, the token owner — delegate.xyz honored — or a named address/contract) is a code project: declare them with `--schema key:Type:Auth`, set them later with `abx configure-param`, and every declared key and every set value reads **straight off the contract** — no resolver, no indexer, no metadata JSON in the middle (`abx state` for the schemas, `abx tokens` for the values). They work on the JS lane *and* the in-chain Solidity renderer lane. Full catalog: [Code projects → PostParam schema](reference/code-projects.md#postparam-schema--the-type--auth-catalog).
|
|
65
104
|
|
|
66
|
-
> **⚠ On an edition (`--copies`), a holder-writable param is SHARED — say this before the creator commits.** Params belong to the **id**,
|
|
105
|
+
> **⚠ On an edition (`--copies`), a holder-writable param is SHARED — say this before the creator commits.** Params belong to the **id**, so one value serves every holder of it and the **last writer wins**; `TokenOwner` means *any* holder. A "name your copy" schema renames the work for all 1,000. Per-collector state needs **one id per copy** (a 721 Series, or `--copies 1` ids); communal state is what shared params are *good* at. A holder-writable `String`/`Bytes` key also has **no on-chain size cap**, so one holder can bloat that id's `tokenURI` past what RPCs serve, for everyone, permanently if a `lock=` then bites — `deploy-code --copies` warns, relay it. Why it is deliberate, and the full shape → [code-projects.md → shared params](reference/code-projects.md#postparam-schema--the-type--auth-catalog).
|
|
67
106
|
|
|
68
107
|
**Planning a priced primary sale? Decide 1/1 vs Series (or edition) BEFORE deploying — it's irreversible.** The shared fixed-price minter sells a **Series** (mint-on-purchase); a plain `abx deploy` **1/1 has no minter/pause/payee**, so its only post-mint move is `abx transfer` (settle an off-chain sale). To run a native fixed-price sale of even a *single* piece, deploy it as a **1-token Series** (`abx deploy-series --count 1`) — or, if copies of that one piece are fine, as an **edition** (`abx deploy --copies <n|open>`), which ships the full sale stack (minter/pause/payee) on its own, no Series wrapper needed. abx has **no secondary-listing feature** — reselling a held token means an external marketplace or a manual `transfer`. Full detail: [operating.md → Selling](reference/operating.md#selling--the-shared-fixed-price-minter).
|
|
69
108
|
|
|
70
109
|
## Phase 0 — make the work first (skip every gate below until it's good)
|
|
71
110
|
|
|
72
|
-
**If the creator is still making the piece, you are in the studio, not in a deploy. Stay there until they
|
|
111
|
+
**If the creator is still making the piece, you are in the studio, not in a deploy. Stay there until they
|
|
112
|
+
say ship.**
|
|
73
113
|
|
|
74
|
-
**
|
|
75
|
-
|
|
76
|
-
- **
|
|
114
|
+
- **Handed a finished file** (`sketch.js`, a build dir, an image folder) → skip to
|
|
115
|
+
[Gate 1](#gate-1--demo-or-real-launch).
|
|
116
|
+
- **Brought an idea, a reference, a vibe, "let's make one together"** → Phase 0.
|
|
77
117
|
|
|
78
|
-
**
|
|
118
|
+
**OFF the table until they ship** — don't ask, don't "just quickly confirm", don't pre-emptively lay out
|
|
119
|
+
tradeoffs: hosting/lane, thumbnails, traits, storage, names, symbols, royalties, gas, minting.
|
|
79
120
|
|
|
80
|
-
**The loop**
|
|
121
|
+
**The loop** (depth → [code-projects.md → Studio loop](reference/code-projects.md#studio-loop--iterate-on-the-work-before-you-deploy-anything)):
|
|
81
122
|
|
|
82
|
-
1. **Write against the real runtime contract from the first draft** — `abx.tokenData.seed` for
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
123
|
+
1. **Write against the real runtime contract from the first draft** — `abx.tokenData.seed` for
|
|
124
|
+
randomness, `abx.traits({…})` for features. Never `Math.random()` "for now"; retrofitting is a rewrite.
|
|
125
|
+
2. **`abx preview --script art.js`, hand them the URL.** Same document the generator serves. This is the
|
|
126
|
+
one place you hand over a link instead of running it for them.
|
|
127
|
+
3. **Edit, tell them to refresh.** The program is re-read from disk per render — no restart, no watcher.
|
|
128
|
+
`--shoot <dir>` gives you frames to check your own work.
|
|
129
|
+
4. **Take feedback, go again.** Several rounds is the point, not scope creep.
|
|
86
130
|
|
|
87
|
-
**Exit only on an explicit ship signal** ("let's deploy this", "I'm happy with it"). Then
|
|
131
|
+
**Exit only on an explicit ship signal** ("let's deploy this", "I'm happy with it"). Then `abx inspect
|
|
132
|
+
<script>` and open the deploy decisions.
|
|
88
133
|
|
|
89
134
|
## Gate 1 — demo or real launch?
|
|
90
135
|
|
|
@@ -108,25 +153,38 @@ Every write builds an unsigned tx; pick the lane by stakes:
|
|
|
108
153
|
| **Wallet** | `--sign` | human's own wallet (MetaMask/Ledger) | real value · key shouldn't touch `.env` |
|
|
109
154
|
| **Cold** | `--unsigned` | multisig / offline signer | a Safe / advanced setup |
|
|
110
155
|
|
|
111
|
-
- **
|
|
112
|
-
|
|
113
|
-
- **
|
|
114
|
-
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
156
|
+
- **Pick it:** you hold the key + low stakes → hot. Human owns the valuable wallet → wallet. Multisig →
|
|
157
|
+
cold. Beyond throwaway testnet, lead with `--sign`.
|
|
158
|
+
- **A missing key is a fork, not a blocker** — offer both: add a funded key, or `--sign` in a browser.
|
|
159
|
+
- **On the wallet lane ALWAYS ask "which wallet will you connect?" and pass `--for <addr>`.** One connect
|
|
160
|
+
assigns **all three roles at once** — owner, royalty receiver, mint recipient — so never offer "connect
|
|
161
|
+
whatever". When you ask, say what you're asking for: **`--for` takes their PUBLIC address (`0x…`), the
|
|
162
|
+
one they'd paste to receive a transfer — never a private key or seed phrase.** A creator nervous about
|
|
163
|
+
crypto hears "give me your address" as "give me your keys" unless you say otherwise.
|
|
164
|
+
- **The sign page connects an INJECTED browser wallet only** (`window.ethereum`) — no WalletConnect, no QR.
|
|
165
|
+
A hardware wallet works through a browser extension that exposes it (MetaMask with a Ledger attached,
|
|
166
|
+
say), not by scanning a code. Say that before they go looking for a QR.
|
|
167
|
+
- **`--for` is a DEPLOY concern only.** Owner ops already sign as the current on-chain owner; passing it
|
|
168
|
+
there is harmless but ignored.
|
|
169
|
+
- **One EOA, serialize every send** — two processes sharing a key pick the same nonce and one dies
|
|
170
|
+
`replacement transaction underpriced`.
|
|
171
|
+
|
|
172
|
+
**⚠ `--sign` BLOCKS until the human signs — ALWAYS background it with `--sign-url-file`, NEVER
|
|
173
|
+
foreground** (a foreground run hangs your whole turn):
|
|
174
|
+
|
|
175
|
+
1. Background `--sign --sign-url-file <path>`.
|
|
176
|
+
2. Read the URL **from that file every time — never assume the port** (each op gets a fresh server).
|
|
177
|
+
3. Relay: "Open `<url>`, connect your wallet, approve." Don't auto-open. Multi-tx → say it's N approvals.
|
|
178
|
+
4. Let the background command finish — it signs, confirms and re-indexes. Don't kill it.
|
|
179
|
+
|
|
180
|
+
**⚠ Wallet-lane ops run ONE AT A TIME** — never start the next `--sign` op until the previous background
|
|
181
|
+
command has finished.
|
|
182
|
+
|
|
183
|
+
- **The sign page is operation-aware** — decoded intent, gated on network + signing wallet + the tx; the
|
|
184
|
+
CLI refuses a mismatched signer.
|
|
185
|
+
- **Multi-tx signs in ONE session** (hot + wallet only): the human connects once and walks the sequence.
|
|
186
|
+
- **Keep tx count low and say it up front** — every preview prints `approvals N wallet approval(s)`, the
|
|
187
|
+
exact signature count.
|
|
130
188
|
|
|
131
189
|
## Quick start
|
|
132
190
|
|
|
@@ -139,7 +197,7 @@ Every write builds an unsigned tx; pick the lane by stakes:
|
|
|
139
197
|
|
|
140
198
|
On-chain's edge past small is self-resolution/permanence, **never cost** — don't call it "cheaper" above the threshold.
|
|
141
199
|
|
|
142
|
-
**The real ceiling is READ gas, not storage cost — and 40 KB is the number, not 256 KB.** `tokenURI` reassembles the whole document per call at
|
|
200
|
+
**The real ceiling is READ gas, not storage cost — and 40 KB is the number, not 256 KB.** `tokenURI` reassembles the whole document per call at **~360–405k gas per KB** (quote the range, never one flat rate). So **per token**: **≲40 KB reads fine** (~15M gas) · **40–100 KB warns** (needs a high-gas RPC) · **>100 KB is refused** — a margin below geth's 50M `eth_call` default, because hosted providers cap lower and marketplaces would see a **revert**, not the work. `--compress fastlz` makes it cheaper to *write*, not to *read*. The override `--allow-unreadable-onchain` is a deliberate creator choice ("almost nothing will display your work") — get an explicit yes, never use it to clear a warning; the usual answer is `--backend arweave` instead. Being per token, a 300-piece collection of 5 KB SVGs is fine. Measured table → [decisions.md → The READ ceiling](reference/decisions.md#the-read-ceiling--the-number-that-actually-decides-can-this-go-on-chain).
|
|
143
201
|
|
|
144
202
|
**When the user names off-chain custody for tiny content ("deploy it as an IPFS NFT"), lead with the on-chain recommendation in your *first* reply** — don't bury it, and don't collect resolver-URL details for a path you're about to advise against. "For a 2.4 KB SVG I'd go fully on-chain — no server, renders forever, cheaper. Want that, or IPFS?" Then let them choose (you surface the better default; you don't override the request). Defaulting to "IPFS" for tiny content is what lands it at a broken localhost URI.
|
|
145
203
|
|
|
@@ -165,30 +223,56 @@ abx deploy-series --dir <media-dir> --name "…" --symbol … [--onchain-uri --b
|
|
|
165
223
|
```bash
|
|
166
224
|
abx deploy-series --dir ./photos --name "My Series" --symbol MS --onchain-uri --backend arweave --mint-all --sign
|
|
167
225
|
```
|
|
168
|
-
Files natural-sort into **tokens `0…N-1`** (`--count N` uses the first N); a token's metadata is its token id.
|
|
226
|
+
Files natural-sort into **tokens `0…N-1`** (`--count N` uses the first N); a token's metadata is its token id. **Content placement is per token — the same custody call as a 1/1** (see [Decisions](#decisions-real-launch)). The one Series-specific win: a **same-extension folder** uploads as ONE directory → a single collection-scope image field (O(1) on-chain, any size); mixed extensions fall back to per-token fields (still no server). **Per-token traits: `--attributes <file.json>`**, lane-aware exactly like a 1/1's — off-chain by default, inlined on-chain under `--onchain-uri`/`--traits-onchain`. Accepted shapes, and the huge-series `set-field` route → [operating.md → Series-only owner ops](reference/operating.md#series-only-owner-ops). *(Traits computed from a seed are a code project, not a static Series.)*
|
|
169
227
|
|
|
170
228
|
## Code projects (generative / code-based drops)
|
|
171
229
|
|
|
172
|
-
A **program is the content** (`abx deploy-code`
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
**
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
**
|
|
181
|
-
|
|
182
|
-
|
|
230
|
+
A **program is the content** (`abx deploy-code`): output is a function of live on-chain state
|
|
231
|
+
(`tokenData`: coordinates + `seed` + PostParams), injected at view time. Everything from a
|
|
232
|
+
[Series](#series-multi-token-drops) applies. This section is the **decision tree**; operating depth —
|
|
233
|
+
what to keep running, the resume loop, verify steps, render ops, lane internals, selling — lives in
|
|
234
|
+
**[reference/code-projects.md](reference/code-projects.md)**.
|
|
235
|
+
|
|
236
|
+
**The mint `seed` is pseudorandom, NOT lottery-grade — say so before a creator prices scarcity off it.** Every mint draws it from the canonical `AbxSeedSource` (block values + token id). It replays afterwards (that is what makes the output verifiable) but is **not secret beforehand**: a contract minting in the same tx can compute the seed it would get and revert unless it likes it. Fine when the seed diversifies the work and the distribution *is* the product; **not** fine for a raffle or any drop where one rare outcome is worth materially more than mint price — that needs the creator's own `IAbxSeedSource` (commit-reveal/VRF), a first-class swap at both ends (`deploy-code --seed-source 0x…`, `abx set-seed-source`). Who picks differs by lane, and a `seed` schema lets an authorized party choose the value outright → [code-projects.md → Seeds](reference/code-projects.md#seeds--pseudorandom-not-lottery-grade).
|
|
237
|
+
|
|
238
|
+
**Infra fork FIRST, before any lane talk: a code project's thumbnail is *rendered* off-chain, so it
|
|
239
|
+
ALWAYS needs a PUBLIC home you provide.** There is NO zero-infrastructure code drop, and "fully
|
|
240
|
+
on-chain" does NOT mean "nothing to run". Two shapes — an **off-chain resolver** (`--public-base-url`,
|
|
241
|
+
the default for a drop you'll sell: auto-renders every mint and param change, stays maneuverable; a
|
|
242
|
+
managed provider with `render.attached` covers both halves with one key), or **fully on-chain**
|
|
243
|
+
(`--onchain-uri --image-base <a bucket you own>`: maximal durability, no always-on service, but stills
|
|
244
|
+
are manual, on-chain traits need a deployed renderer, and the whole document rides each `tokenURI` at
|
|
245
|
+
~360–405k gas/KB — `deploy-code --dry-run` prints the figure, relay it). Settle this with the creator
|
|
246
|
+
first. *(The one zero-infra-AND-on-chain exception is the in-chain Solidity lane below.)*
|
|
247
|
+
|
|
248
|
+
**Writing the program yourself? There is ONE runtime contract — get it right or the drop is silently
|
|
249
|
+
broken** (seed never injects → every token identical; traits empty). The program reads state via
|
|
250
|
+
**`abx.tokenData`** (flat: `.seed`, and each `--schema` key) and reports traits via **`abx.traits({…})`**
|
|
251
|
+
— never an invented global, never "defensively across variants". `abx.traits()` is the ONLY thing
|
|
252
|
+
captured into `attributes`, on the resolver lane too. Verify with `abx inspect` before picking a lane:
|
|
253
|
+
its PostParams and Traits lines reflect what the program *actually* reads and reports. Full contract →
|
|
254
|
+
[code-projects.md → Authoring the program](reference/code-projects.md#authoring-the-program--the-abxjs-runtime-contract-get-this-right-first).
|
|
255
|
+
|
|
256
|
+
**Run `abx inspect <script>` before proposing any lane, and adopt the lane it recommends.** A code
|
|
257
|
+
project has surfaces that must each land somewhere public, and the fatal mistake is picking "fully
|
|
258
|
+
on-chain, no server!" then discovering one at a time, after deploy, that it carries no thumbnail, no
|
|
259
|
+
traits, and dropped a PostParam. **Settle these BEFORE you deploy.** They are not impossible to
|
|
260
|
+
change afterwards — the honest cost is an owner-signed **re-point tx** (`set-field image <public url>`)
|
|
261
|
+
plus a re-render, on a collection that has been showing a placeholder in the meantime. `abx inspect` and `deploy-code --dry-run` print a **Surfaces block** grading
|
|
262
|
+
each one; **every ⚠ there is a marketplace-facing hole to close before deploy — trust it over your own
|
|
263
|
+
read.** Resolve them into ONE coherent lane before collecting identity or showing a config:
|
|
183
264
|
|
|
184
265
|
| Surface | On-chain | Off-chain |
|
|
185
266
|
|---|---|---|
|
|
186
267
|
| **tokenURI + animation** | `--onchain-uri` — *if* script + every dep fit one `tokenURI` eth_call (`abx inspect` estimates) | a resolver (`--public-base-url`) |
|
|
187
|
-
| **thumbnail (`image`)** | ❌ never computed on-chain — needs a public destination: `--image-base <S3/R2/CDN you own>`. **No destination ⇒ placeholder
|
|
268
|
+
| **thumbnail (`image`)** | ❌ never computed on-chain — needs a public destination: `--image-base <S3/R2/CDN you own>`. **No destination ⇒ placeholder + an orphaned render until you re-point (`set-field image` + re-render, owner-signed). Never localhost.** | a resolver's `/image` |
|
|
188
269
|
| **traits (`attributes`)** | a Solidity `--attributes-renderer 0x…` you **DEPLOY** (fork `SeedTraitsRenderer`) — **not a free flag or guessable address** (`deploy-code` refuses a codeless one; "ports to Solidity" ≠ "deployed") | a resolver stitches the JS `abx.traits()` |
|
|
189
270
|
| **PostParams** | declare EVERY key the script reads: `--schema key:Type:Auth` (a palette collectors set = `palette:HexColor:TokenOwner`) — else silently dropped at render | — |
|
|
190
271
|
|
|
191
|
-
**The resolver is the pivot: choosing *no resolver* forecloses the off-chain column for thumbnail
|
|
272
|
+
**The resolver is the pivot: choosing *no resolver* forecloses the off-chain column for thumbnail and
|
|
273
|
+
traits.** (On `--onchain-uri`, an unauthenticated Etherscan "Read Contract" can hit its gas cap on a big
|
|
274
|
+
document and look like a revert — expected; verify with `abx tokenuri`, never `abx index --full` to
|
|
275
|
+
"fix" it. → [troubleshooting.md](reference/troubleshooting.md).)
|
|
192
276
|
|
|
193
277
|
**Pick your code lane** (all `abx deploy-code`):
|
|
194
278
|
|
|
@@ -201,30 +285,40 @@ A **program is the content** (`abx deploy-code` → a `SeriesCode`): output is a
|
|
|
201
285
|
| A **Solidity renderer** (SVG on-chain, from seed+params) | `--image-renderer 0x<svg> [--attributes-renderer 0x<traits>] --onchain-uri` (no `--script`) | **truly in-chain** — image + traits computed on-chain, tokenURI on-chain, **zero infra**; no `animation_url` (no program) |
|
|
202
286
|
| **A JS program AND on-chain thumbnail/traits** — the both-worlds shape | `--script f.js --image-renderer 0x<svg> --attributes-renderer 0x<traits> --onchain-uri` | **every surface in-chain, zero infra** — `animation_url` assembles from the script chunks, `image`+`attributes` computed by Solidity. **Nothing to render or host.** |
|
|
203
287
|
|
|
204
|
-
**`--script` and `--image-renderer` are NOT an either-or
|
|
288
|
+
**`--script` and `--image-renderer` are NOT an either-or** — passing both is the strongest shape
|
|
289
|
+
available and the most-missed: the program drives `animation_url` on-chain while Solidity renderers
|
|
290
|
+
supply `image` and `attributes`, so an interactive generative piece gets a real thumbnail and real
|
|
291
|
+
traits with **no resolver, no runner, no bucket**.
|
|
205
292
|
|
|
206
|
-
**⭐
|
|
293
|
+
**⭐ The in-chain Solidity lane — recommend on-chain `tokenURI` without hesitation.** The work is a
|
|
294
|
+
Solidity `IAbxFieldRenderer` returning an SVG from seed+params, wired with no `--script`: image and
|
|
295
|
+
traits computed on-chain, **genuinely nothing to run**. **Not no-code, though** — the renderer is a
|
|
296
|
+
contract the creator compiles and deploys with Foundry (`abx scaffold-renderer <dir>` writes a
|
|
297
|
+
ready-to-build project; `abx` never compiles or deploys Solidity). Not comfortable with Solidity? Steer
|
|
298
|
+
to the resolver, or `--onchain-image` for a tiny static SVG. Depth →
|
|
299
|
+
[code-projects.md](reference/code-projects.md#in-chain-solidity-svg--the-zero-dependency-lane).
|
|
207
300
|
|
|
208
301
|
## Decisions (real launch)
|
|
209
302
|
|
|
210
|
-
|
|
303
|
+
**Custody is the master call** — size decides it ([Quick start](#quick-start) has the rule and the READ
|
|
304
|
+
ceiling; do not restate the numbers, they live there). Four patterns, by where bytes live × how
|
|
305
|
+
`tokenURI` resolves — name the one you're proposing, then configure it from
|
|
306
|
+
[decisions.md](reference/decisions.md):
|
|
211
307
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
308
|
+
1. **Fully on-chain** (`--onchain-image`) — bytes *and* JSON on-chain. Small content only.
|
|
309
|
+
2. **Image off-chain, JSON on-chain, no server** (`--onchain-uri --backend arweave|ipfs|cloud`) — the
|
|
310
|
+
sweet spot for static content.
|
|
311
|
+
3. **Remote resolver** (`--public-base-url` + a node) — mutable/dynamic metadata; self-hosted or managed.
|
|
312
|
+
4. **Inline SVG on-chain** — self-contained vector. 1/1 → `--onchain-uri`; a Series of SVGs →
|
|
313
|
+
`--onchain-image --compress fastlz`.
|
|
216
314
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
1. **Fully on-chain** (`--onchain-image`) — bytes + JSON on-chain. Small content only (≲ 40 KB/token to read).
|
|
220
|
-
2. **Image off-chain, JSON on-chain, no server** (`--onchain-uri --backend arweave|ipfs|cloud`) — the sweet spot for static content.
|
|
221
|
-
3. **Remote resolver** (`--public-base-url` + a node) — mutable/dynamic metadata; self-hosted or a managed provider.
|
|
222
|
-
4. **Inline SVG on-chain** — self-contained vector content. 1/1 → `--onchain-uri`; a Series of SVGs → `--onchain-image --compress fastlz`.
|
|
315
|
+
The second axis is **mutability**: any pattern stays editable until you freeze it, so ship unlocked,
|
|
316
|
+
confirm it resolves, then lock (see *Store ≠ lock* below).
|
|
223
317
|
|
|
224
|
-
**IPFS/Arweave is NOT a server.** Pattern 2 commits the bare CID/txid on-chain and the renderer joins
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
318
|
+
**IPFS/Arweave is NOT a server.** Pattern 2 commits the bare CID/txid on-chain and the renderer joins it
|
|
319
|
+
to the collection's gateway prefix at read time — nothing to keep running, and the gateway is a one-tx
|
|
320
|
+
repoint (`abx set-gateway`), not a value welded into the field. Only pattern 3 needs a resolver. Never
|
|
321
|
+
tell a creator IPFS is blocked on "a public URL" or "a server always online".
|
|
228
322
|
|
|
229
323
|
**The six decisions** — storage permanence · public host URL · identity · image placement · on-chain
|
|
230
324
|
vs off-chain resolution · when to mint. Full detail, tradeoffs and failure modes:
|
|
@@ -233,14 +327,13 @@ vs off-chain resolution · when to mint. Full detail, tradeoffs and failure mode
|
|
|
233
327
|
- **Never bake localhost** into an off-chain deploy — that token resolves for no one. The CLI refuses it; don't try to talk it round.
|
|
234
328
|
- **Propose a real name/symbol and get an explicit yes** — on-chain identity is effectively permanent, and a generic folder name infers junk. The CLI refuses a real send that would bake its own placeholder (a `--dry-run` only warns — that is not permission).
|
|
235
329
|
- **Store ≠ lock; lock last.** Deploy unlocked, confirm it resolves in production, *then* freeze (`lock-field` / `lock-uri`; a code drop also needs `lock-script` + `lock-dependencies`). A deliberate follow-up, never the first deploy.
|
|
236
|
-
- **Say "locked metadata", never "immutable work".** Locks freeze what the contract *stores
|
|
330
|
+
- **Say "locked metadata", never "immutable work".** Locks freeze what the contract *stores* — and every one freezes a **pointer or a value, never behavior** (a locked renderer can still be a proxy that is upgraded later). An **ungoverned PostParam has no lock at all**; a schema'd one can be welded. The lock a **buyer** actually asks about is `lock-param-hooks`, because a `--transfer` hook is a **veto** over whether they can ever sell. A live-adapting token is a fine thing to build — just never sell a frozen one as frozen unless it is. Rules, exceptions, and what to check before any permanence claim → [decisions.md → Locks](reference/decisions.md#locks--what-actually-freezes-and-what-a-buyer-is-really-asking).
|
|
237
331
|
- **Tunnels (ngrok/cloudflared) are preview-only** — never bake one on-chain.
|
|
238
332
|
|
|
239
333
|
## Confirm before sending
|
|
240
334
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
**No signing key in `.env` yet? `--dry-run` still needs a deployer address — pass `--for 0x<the creator's wallet>`.** The address is a pure function of (factory, salt, deployer), so a preview can't compute it from nothing; it signs nothing, so no key is involved. Ask the creator for their wallet address once, up front — it's also what the real wallet-lane deploy takes (`--sign --for 0x…`).
|
|
335
|
+
`abx deploy --dry-run` for real values (pin the salt it prints with a second `--dry-run --salt …`).
|
|
336
|
+
**No key in `.env`? `--dry-run` still needs a deployer — pass `--for 0x<the creator's wallet>`.**
|
|
244
337
|
|
|
245
338
|
```
|
|
246
339
|
Deploy config — confirm before I send (everything below is written on-chain):
|
|
@@ -262,17 +355,23 @@ Deploy config — confirm before I send (everything below is written on-chain):
|
|
|
262
355
|
```
|
|
263
356
|
|
|
264
357
|
Rules:
|
|
265
|
-
- **The readout is a contract: what's shown is *exactly* what deploys.** Every value
|
|
266
|
-
|
|
267
|
-
- **
|
|
268
|
-
|
|
269
|
-
- **
|
|
270
|
-
- **
|
|
271
|
-
-
|
|
358
|
+
- **The readout is a contract: what's shown is *exactly* what deploys.** Every value is a flag you pass;
|
|
359
|
+
anything not going on-chain says so.
|
|
360
|
+
- **Every line is verified, never aspirational.** A value depending on a contract that must already exist
|
|
361
|
+
(`--attributes-renderer`, a minter) is only claimed once checked.
|
|
362
|
+
- **Flag every inferred or defaulted value** with `⚠` and where it came from.
|
|
363
|
+
- **The description is the one people forget** — never deploy an auto-written or empty one quietly.
|
|
364
|
+
- **Traits are the creator's** — ask; never invent them.
|
|
365
|
+
- **Show real values**: real name, description, address + pinned salt, chunk/tx counts from the dry run.
|
|
366
|
+
- Adapt rows to the config (off-chain shows backend + host URL instead of the renderer; `--no-mint` shows
|
|
367
|
+
a deferred mint) — but always show what is written on-chain.
|
|
272
368
|
|
|
273
369
|
## Deploy strategy — when to mint
|
|
274
370
|
|
|
275
|
-
The deploy address is **deterministic** — a pure function of `(factory, salt)`, knowable before signing
|
|
371
|
+
The deploy address is **deterministic** — a pure function of `(factory, salt)`, knowable before signing
|
|
372
|
+
(`abx predict`). So you can warm a resolver at that exact address first, and the moment a marketplace
|
|
373
|
+
sees the mint it fetches live metadata rather than a cached blank. Minting at deploy is optional
|
|
374
|
+
(`--no-mint`); `abx mint` is the one-shot.
|
|
276
375
|
|
|
277
376
|
| Path | When | Flow |
|
|
278
377
|
|---|---|---|
|
|
@@ -280,115 +379,110 @@ The deploy address is **deterministic** — a pure function of `(factory, salt)`
|
|
|
280
379
|
| **Careful** *(real launch, off-chain resolver)* | metadata live the instant it's listable | `deploy --no-mint` → `serve` (warm) → verify → `mint` → `refresh` |
|
|
281
380
|
| **Primary sale** | token issued at point of sale | `deploy --no-mint` → settle off-chain → `mint --to <buyer>` → `refresh` |
|
|
282
381
|
|
|
283
|
-
- **Fully on-chain ⇒ mint at deploy; do NOT add `--no-mint`.** The careful path warms an *off-chain
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
- **Etherscan source is already verified** — your deploys are EIP-1167 clones Etherscan auto-recognizes as proxies of the verified implementation.
|
|
290
|
-
|
|
291
|
-
## List in the ABX App Store (optional)
|
|
292
|
-
|
|
293
|
-
**Not part of deploy.** After the collection is live, ask: *want this listed in the ABX App Store?* If no, stop. If yes, this is a second, explicit opt-in — minting a token in the store registry — not marketplace discovery.
|
|
382
|
+
- **Fully on-chain ⇒ mint at deploy; do NOT add `--no-mint`.** The careful path warms an *off-chain
|
|
383
|
+
resolver*; a fully on-chain token has none — the renderer resolves the instant the contract exists, so
|
|
384
|
+
deferring buys nothing and costs a tx. Defer an on-chain mint only for a genuine primary sale.
|
|
385
|
+
- **There is no "add it to a marketplace" step — don't offer one.** Marketplaces discover the collection
|
|
386
|
+
from chain (the mint, plus ERC-4906 on either lane); `abx refresh` — and `abx ping-uri` on an edition —
|
|
387
|
+
is the only nudge. The App Store is a separate opt-in, never folded into deploy.
|
|
294
388
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
```bash
|
|
299
|
-
abx submit-app <collection> \
|
|
300
|
-
--name "…" --summary "…" --description "…" \
|
|
301
|
-
--category Create --stage Prototype \
|
|
302
|
-
--url https://… \
|
|
303
|
-
--sign
|
|
304
|
-
```
|
|
389
|
+
`abx predict` and salt guards, the `--dry-run`-without-`--salt` rule, ERC-4906 vs the edition's native
|
|
390
|
+
`URI` event, and why Etherscan already shows verified source →
|
|
391
|
+
[operating.md → Deploy strategy](reference/operating.md#deploy-strategy--the-mechanics).
|
|
305
392
|
|
|
306
|
-
|
|
307
|
-
4. Humans can instead use the store's `/submit` page; same txs. Don't hand them a list of `configure-param` commands.
|
|
393
|
+
## List in the ABX App Store (optional)
|
|
308
394
|
|
|
309
|
-
|
|
395
|
+
**Not part of deploy, ever.** After the collection is live, ask once: *want this listed in the ABX App
|
|
396
|
+
Store?* If no, stop. If yes it is a second, explicit opt-in — minting a token in the store registry —
|
|
397
|
+
and **not** marketplace discovery. Draft the store copy *with* the creator (`--name`/`--summary`/
|
|
398
|
+
`--description` describe what someone can **do**, not the NFT's metadata — never silently reuse the
|
|
399
|
+
deploy's). You run `abx submit-app`, `--dry-run` first. The wallet must **own the collection** at mint.
|
|
400
|
+
Full command, the multi-tx write, and the humans-use-`/submit` alternative →
|
|
401
|
+
[operating.md → App Store](reference/operating.md#listing-in-the-abx-app-store--abx-submit-app).
|
|
310
402
|
|
|
311
403
|
## What a token carries — files beyond the image (the data plane)
|
|
312
404
|
|
|
313
|
-
A token is **not "just a picture."** It anchors **named, typed files
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
- **`artifacts` is COMPUTED, never a field you set
|
|
326
|
-
- **
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
-
|
|
330
|
-
|
|
405
|
+
A token is **not "just a picture."** It anchors **named, typed files**, and the served JSON carries an
|
|
406
|
+
**`artifacts`** list — the *complete* set, each `{key, mimeType, uri}`. `image`/`animation_url` are
|
|
407
|
+
reserved members of that same set; alongside them a token can carry a hi-res master, a certificate,
|
|
408
|
+
source files, a README.
|
|
409
|
+
|
|
410
|
+
- **Attach:** `abx attach <addr> <key> <ipfs://… | ar://… | https://…>` — `<key>` is any name you pick.
|
|
411
|
+
Representation is auto-detected from the scheme; `mimeType` comes from the file **extension**, so
|
|
412
|
+
point the URI at the file itself. Tiny bytes with no host can go on-chain with `--file`. One file per
|
|
413
|
+
call, after deploy.
|
|
414
|
+
- **No URL yet?** `abx storage upload <path> --backend arweave|ipfs` prints the locator `attach` wants
|
|
415
|
+
(and the ready-to-run `attach` line). Hand `attach` the `ipfs://`/`ar://` form, never a gateway URL —
|
|
416
|
+
an `https://` locator is stored as an opaque `url` with the host welded in.
|
|
417
|
+
- **`artifacts` is COMPUTED, never a field you set** — the manifest builds itself from your fields.
|
|
418
|
+
- **Say the honest limit up front:** no mainstream marketplace shows a files tab **today**. Verify an
|
|
419
|
+
attach with `abx tokenuri <addr> --fetch`, not by refreshing OpenSea.
|
|
420
|
+
|
|
421
|
+
Resolver-vs-on-chain reach, effect outputs, reserved keys, and the params exception →
|
|
422
|
+
[operating.md → Attaching files](reference/operating.md#attaching-files--the-data-plane).
|
|
331
423
|
|
|
332
424
|
## After launch — tell the creator (durability + owner care)
|
|
333
425
|
|
|
334
|
-
|
|
426
|
+
Cover these in plain language once it's live; don't wait to be asked.
|
|
335
427
|
|
|
336
|
-
- **Durability
|
|
337
|
-
|
|
338
|
-
|
|
428
|
+
- **Durability is the backend's, and they differ.** Arweave = pay once, kept for centuries, nothing to
|
|
429
|
+
renew. IPFS = **only as durable as the pin** — if pinning lapses the bytes can go. `cloud`/`fs` = they
|
|
430
|
+
maintain it. Say which one this drop uses and what it implies.
|
|
431
|
+
- **Gateway ≠ backend, and the sizes differ.** A slow or dead gateway for ipfs/arweave content is **one
|
|
432
|
+
tx** (`abx set-gateway`), no re-upload, and it works on locked fields. Changing the **backend** is a
|
|
433
|
+
real migration: re-upload, then re-point. Both are owner-signed.
|
|
434
|
+
- **Owner-wallet hygiene** (offer as follow-ons): the owner wallet controls mint, royalties, URIs and
|
|
435
|
+
ownership itself — recommend a hardware/dedicated wallet over a hot key, a multisig
|
|
436
|
+
(`abx set-admin`) for anything valuable, and backing up `.abx-self-host/arweave-key.json` if Arweave
|
|
437
|
+
credits live there.
|
|
339
438
|
|
|
340
439
|
## Setup + environment
|
|
341
440
|
|
|
342
|
-
`abx` needs **Node ≥ 22.5
|
|
441
|
+
`abx` needs **Node ≥ 22.5**. Resolve the binary **local first, then global** — `./node_modules/.bin/abx`
|
|
442
|
+
(preferred; invoke as `npx abx …`), else a global `abx`, else install **`@artblocks/abx-cli`** (never
|
|
443
|
+
`@artblocks/abx-sdk`, which ships no binary). **Never probe with a bare `npx abx`** — that name on npm is
|
|
444
|
+
an unrelated squatted package, and `--no-install` does not make it safe. Ask before installing globally;
|
|
445
|
+
keep one invocation for the whole session. In the abx source repo: `pnpm abx <cmd>`.
|
|
343
446
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
```bash
|
|
349
|
-
./node_modules/.bin/abx version # 1. project-local — PREFER this (invoke as `npx abx <cmd>` once you know it's there)
|
|
350
|
-
abx version # 2. a global install already on PATH
|
|
351
|
-
```
|
|
447
|
+
`.env` is **secrets only** — `ABX_DEPLOYER_PK` (hot lane only; prefer `--sign` and no key at all),
|
|
448
|
+
`ABX_RPC_URLS`, `ABX_PUBLIC_BASE_URL`, backend secrets, and the one client credential grammar
|
|
449
|
+
`ABX_REMOTE_<NAME>_URL` + `ABX_REMOTE_<NAME>_TOKEN` (a node you run is the name `self`).
|
|
352
450
|
|
|
353
|
-
|
|
354
|
-
|
|
451
|
+
Install matrix, the npx-cache trap in full, every env var, RPC range caps and pruned log history →
|
|
452
|
+
[reference/setup.md](reference/setup.md).
|
|
355
453
|
|
|
356
|
-
**
|
|
454
|
+
**If indexing is slow, or a hosted resolver won't serve, ASK IT: `abx status <addr> [--remote <name>]`**
|
|
455
|
+
reports the lifecycle (`queued`→`backfilling`→`live`, plus `stale`/`failed` with a machine-readable
|
|
456
|
+
cause), so "still catching up" and "broken" stop looking alike. **Check the scan floor (is it scanning
|
|
457
|
+
from block 0?) before blaming the RPC tier** — that mis-diagnosis is a known trap.
|
|
357
458
|
|
|
358
|
-
|
|
359
|
-
|---|---|---|
|
|
360
|
-
| The creator has a project dir (a `package.json`) — **default** | `npm install --save-dev @artblocks/abx-cli` | `npx abx …` |
|
|
361
|
-
| No project, or they asked for a machine-wide tool | `npm install -g @artblocks/abx-cli` | `abx …` |
|
|
362
|
-
|
|
363
|
-
Ask before installing **globally** — it's a machine-wide change to their PATH, and the per-project install is the reversible one. A project install needs no permission beyond the usual.
|
|
364
|
-
|
|
365
|
-
Whichever you land on, **keep using that same invocation for every command in the session** (`npx abx …` vs `abx …`) — don't mix them, or you'll silently drive two different CLI versions. Then run `abx doctor` (preflight: signing key/wallet, RPC, canonical factory, storage).
|
|
366
|
-
|
|
367
|
-
`.env` (in the creator's project dir) = **secrets only**:
|
|
368
|
-
- **Signing:** `ABX_DEPLOYER_PK` (the only name read — older `SEPOLIA_FUNDED_PK`/`SEPOLIA_WALLET_PK` are retired, and the CLI's error names them if it finds neither) is needed ONLY for hot/unattended signing. If the creator owns a wallet, prefer **`--sign`** — no key in `.env`. `doctor`'s missing-key ✗ is **not fatal** on the `--sign` path.
|
|
369
|
-
- `ABX_RPC_URLS`, `ABX_PUBLIC_BASE_URL` (remote-resolver custody only), optional `OPENSEA_API_KEY`, plus any backend secret.
|
|
370
|
-
- **One client-side credential grammar: `ABX_REMOTE_<NAME>_URL` + `ABX_REMOTE_<NAME>_TOKEN`** (same normalization as `ABX_RPC_URLS_<CHAIN>`) — a node you run is just the name **`self`** (`deploy-resolver` generates `ABX_REMOTE_SELF_TOKEN` locally; bare `--remote` defaults to it), a managed provider is its own name. `ABX_RESOLVER_ADMIN_TOKEN` is the **resolver's own server-side config** (never read client-side) — a stale copy of it in `.env` with no `ABX_REMOTE_SELF_TOKEN` gets a pointed error naming both vars, and `abx doctor` flags it too. Only `_URL`/`_TOKEN` are read — `ABX_REMOTE_<NAME>_KEY` is ignored (the CLI flags a near-miss name).
|
|
371
|
-
|
|
372
|
-
<sub>Working from the abx **source repo** (contributor)? `pnpm install`, then `pnpm abx <cmd>` or `pnpm sandbox`. Every command below is identical.</sub>
|
|
459
|
+
## Reference files
|
|
373
460
|
|
|
374
|
-
|
|
461
|
+
Load one when the decision tree above sends you there — each is the depth behind a routing line here.
|
|
375
462
|
|
|
376
|
-
|
|
463
|
+
| File | Holds |
|
|
464
|
+
|---|---|
|
|
465
|
+
| [decisions.md](reference/decisions.md) | the six launch decisions in depth · the READ ceiling (measured) · `--copies` custody · **locks: what freezes and what doesn't** |
|
|
466
|
+
| [code-projects.md](reference/code-projects.md) | the `abx.js` runtime contract · studio loop · PostParam catalog · seeds · in-chain Solidity lane · render ops · what to keep running |
|
|
467
|
+
| [operating.md](reference/operating.md) | owner ops · credit + license · **attaching files (data plane)** · selling via the shared minter · mint page · App Store · deploy mechanics · migrating hosting |
|
|
468
|
+
| [hosting.md](reference/hosting.md) | storage backends + Turbo playbook · gateways and their floors · managed providers + named remotes · `deploy-resolver`/`deploy-effects` |
|
|
469
|
+
| [setup.md](reference/setup.md) | install + `.env` (start here first time) · RPC selection, failover, range caps |
|
|
470
|
+
| [troubleshooting.md](reference/troubleshooting.md) | "my NFT looks wrong" — placeholders, stale listings, reverting `tokenURI`, baked localhost, not-registered |
|
|
471
|
+
| [creator-token.md](reference/creator-token.md) | ERC-721C royalty enforcement — **only when the creator raises it**; plain ERC-721 is the default |
|
|
377
472
|
|
|
378
|
-
-
|
|
379
|
-
- **Configuring a real launch — the six decisions in depth** (storage backends + who pays, public host URL + managed vs self-hosted, identity/credit/license, image placement + inline-vs-reader thresholds, on-chain vs off-chain resolution, store-vs-lock) → **[reference/decisions.md](reference/decisions.md)**
|
|
380
|
-
- **Code projects — operating depth** (what to keep running, the resume loop, verify-it-resolves, render ops, `--onchain-uri`/`--image-base`/traits internals, arweave delay, `deploy-code` flags, mint timing/pause/supply) → **[reference/code-projects.md](reference/code-projects.md)**
|
|
381
|
-
- **Operating an existing project** (owner ops, **creator credit + license fields**, **attaching files / the data plane**, selling via the shared minter, `abx mint-page`, moving hosting, resolver→resolver `migrate`) → **[reference/operating.md](reference/operating.md)**
|
|
382
|
-
- **Royalty enforcement / ERC-721C / "make OpenSea honor my royalties" — ONLY when the creator raises it themselves** (plain ERC-721 is the default + recommendation; never offer enforcement unprompted) → **[reference/creator-token.md](reference/creator-token.md)**
|
|
383
|
-
- **Hosting infrastructure** (storage backends, Turbo lanes + failure playbook, **managed providers + named remotes + the service descriptor**, `deploy-resolver`, `deploy-effects`, local-vs-remote stores, token API routes, Docker) → **[reference/hosting.md](reference/hosting.md)**
|
|
384
|
-
- **Environment detail** (RPC selection + failover, multi-chain, troubleshooting) → **[reference/setup.md](reference/setup.md)**
|
|
385
|
-
- **Troubleshooting — "my NFT looks wrong"** (gray placeholder, stale-on-marketplace, tokenURI reverts, localhost baked, "not registered", **registered-but-serving-nothing / indexing status**) — diagnose before acting → **[reference/troubleshooting.md](reference/troubleshooting.md)**
|
|
473
|
+
Human-facing companion: **https://abx.docs.artblocks.io** — send creators there, work from this skill.
|
|
386
474
|
|
|
387
475
|
## Guarantees
|
|
388
476
|
|
|
389
|
-
- **Reconstruction from chain alone** — deterministic
|
|
390
|
-
|
|
391
|
-
- **
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
477
|
+
- **Reconstruction from chain alone** — deterministic and idempotent; delete the projection, replay
|
|
478
|
+
yields identical state.
|
|
479
|
+
- **Trust = the factory.** A clone is canonical only when the ownerless factory's `isAbxClone` confirms
|
|
480
|
+
it. The `AbxDeployed` beacon is discovery and spoofable, and **the shared minters' events are discovery
|
|
481
|
+
too**: both minters price a sale for any contract of the right shape, so a hostile target can make the
|
|
482
|
+
canonical minter emit `Purchase` with no NFT issued. Authenticate the **token** — `isAbxClone`, its own
|
|
483
|
+
`minter()`, its `Transfer`/`TransferSingle` — never the minter's log.
|
|
484
|
+
- **Content is verifiable** — the on-chain keccak256 lets anyone re-hash the served bytes; zero trust in
|
|
485
|
+
the node.
|
|
486
|
+
- **Keys stay with their owner** — the wallet lane signs in the user's own wallet; the CLI never sees the
|
|
487
|
+
key. Runtime data lives in `./.abx-self-host/`, which the CLI makes self-ignoring (it writes a
|
|
488
|
+
`.gitignore` inside on first use) because that directory can hold a signing-capable Arweave key.
|