@artblocks/abx-cli 0.1.0-alpha.3 → 0.1.0-alpha.31
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 +3694 -0
- package/assets/renderer-scaffold/README.md +2 -2
- package/assets/renderer-scaffold/src/MyRenderer.sol +2 -2
- package/assets/renderer-scaffold/src/interfaces/IAbxFieldRenderer.sol +1 -1
- package/assets/renderer-scaffold/src/interfaces/IAbxParams.sol +17 -0
- package/assets/renderer-scaffold/test/MyRenderer.t.sol +51 -2
- package/dist/bin.d.ts +27 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +64 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/deploy.d.ts +242 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +4764 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/project.d.ts +257 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +1299 -0
- package/dist/commands/project.js.map +1 -0
- package/dist/commands/reads.d.ts +64 -0
- package/dist/commands/reads.d.ts.map +1 -0
- package/dist/commands/reads.js +701 -0
- package/dist/commands/reads.js.map +1 -0
- package/dist/commands/scaffold.d.ts +73 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/scaffold.js +679 -0
- package/dist/commands/scaffold.js.map +1 -0
- package/dist/commands/service.d.ts +41 -0
- package/dist/commands/service.d.ts.map +1 -0
- package/dist/commands/service.js +618 -0
- package/dist/commands/service.js.map +1 -0
- package/dist/commands/storage.d.ts +51 -0
- package/dist/commands/storage.d.ts.map +1 -0
- package/dist/commands/storage.js +370 -0
- package/dist/commands/storage.js.map +1 -0
- package/dist/commands/submit-app.d.ts +58 -0
- package/dist/commands/submit-app.d.ts.map +1 -0
- package/dist/commands/submit-app.js +512 -0
- package/dist/commands/submit-app.js.map +1 -0
- package/dist/config.d.ts +90 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +285 -11
- package/dist/config.js.map +1 -1
- package/dist/conformance.d.ts +31 -0
- package/dist/conformance.d.ts.map +1 -0
- package/dist/conformance.js +393 -0
- package/dist/conformance.js.map +1 -0
- package/dist/deps.d.ts +6 -39
- package/dist/deps.d.ts.map +1 -1
- package/dist/deps.js +4 -68
- package/dist/deps.js.map +1 -1
- package/dist/errors.d.ts +20 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +25 -0
- package/dist/errors.js.map +1 -0
- package/dist/flag-allowlists.d.ts +53 -0
- package/dist/flag-allowlists.d.ts.map +1 -0
- package/dist/flag-allowlists.js +157 -0
- package/dist/flag-allowlists.js.map +1 -0
- package/dist/flags.d.ts +45 -0
- package/dist/flags.d.ts.map +1 -1
- package/dist/flags.js +117 -1
- package/dist/flags.js.map +1 -1
- package/dist/jsonout.d.ts +37 -0
- package/dist/jsonout.d.ts.map +1 -0
- package/dist/jsonout.js +68 -0
- package/dist/jsonout.js.map +1 -0
- package/dist/kind.d.ts +61 -0
- package/dist/kind.d.ts.map +1 -0
- package/dist/kind.js +111 -0
- package/dist/kind.js.map +1 -0
- package/dist/main.js +699 -4736
- package/dist/main.js.map +1 -1
- package/dist/mintpage.d.ts +17 -2
- package/dist/mintpage.d.ts.map +1 -1
- package/dist/mintpage.js +241 -54
- package/dist/mintpage.js.map +1 -1
- package/dist/output.d.ts +179 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +756 -0
- package/dist/output.js.map +1 -0
- package/dist/ownerops.d.ts +302 -51
- package/dist/ownerops.d.ts.map +1 -1
- package/dist/ownerops.js +1780 -357
- package/dist/ownerops.js.map +1 -1
- package/dist/preview.d.ts +81 -0
- package/dist/preview.d.ts.map +1 -0
- package/dist/preview.js +535 -0
- package/dist/preview.js.map +1 -0
- package/dist/prompt.d.ts +17 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +19 -0
- package/dist/prompt.js.map +1 -0
- package/dist/provision.d.ts.map +1 -1
- package/dist/provision.js +16 -8
- package/dist/provision.js.map +1 -1
- package/dist/remote.d.ts +153 -52
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +406 -46
- package/dist/remote.js.map +1 -1
- package/dist/riskgate.d.ts +58 -0
- package/dist/riskgate.d.ts.map +1 -0
- package/dist/riskgate.js +212 -0
- package/dist/riskgate.js.map +1 -0
- package/dist/scaffold.d.ts +10 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +52 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/schema.d.ts +36 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +121 -26
- package/dist/schema.js.map +1 -1
- package/dist/script-chunks.d.ts +8 -0
- package/dist/script-chunks.d.ts.map +1 -0
- package/dist/script-chunks.js +35 -0
- package/dist/script-chunks.js.map +1 -0
- package/dist/served.d.ts +30 -0
- package/dist/served.d.ts.map +1 -0
- package/dist/served.js +112 -0
- package/dist/served.js.map +1 -0
- package/dist/signer.d.ts +13 -0
- package/dist/signer.d.ts.map +1 -1
- package/dist/signer.js +84 -15
- package/dist/signer.js.map +1 -1
- package/dist/update-check.d.ts +83 -4
- package/dist/update-check.d.ts.map +1 -1
- package/dist/update-check.js +140 -19
- package/dist/update-check.js.map +1 -1
- package/package.json +12 -8
- package/skill/SKILL.md +351 -151
- package/skill/reference/code-projects.md +183 -26
- package/skill/reference/creator-token.md +99 -0
- package/skill/reference/decisions.md +174 -0
- package/skill/reference/hosting.md +63 -19
- package/skill/reference/operating.md +116 -12
- package/skill/reference/setup.md +57 -6
- package/skill/reference/troubleshooting.md +42 -5
- package/dist/inspect.d.ts +0 -48
- package/dist/inspect.d.ts.map +0 -1
- package/dist/inspect.js +0 -184
- package/dist/inspect.js.map +0 -1
- package/dist/migrate.d.ts +0 -65
- package/dist/migrate.d.ts.map +0 -1
- package/dist/migrate.js +0 -180
- package/dist/migrate.js.map +0 -1
- package/dist/onchain-uri.d.ts +0 -97
- package/dist/onchain-uri.d.ts.map +0 -1
- package/dist/onchain-uri.js +0 -243
- package/dist/onchain-uri.js.map +0 -1
- package/dist/upload.d.ts +0 -28
- package/dist/upload.d.ts.map +0 -1
- package/dist/upload.js +0 -41
- package/dist/upload.js.map +0 -1
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
After launch the owner operates the project. Each command builds a tx, signs it via the [lane](../SKILL.md#gate-2--who-signs-three-lanes) you pick, then re-indexes so the served state updates. All are **owner-only** — the signer must be the contract owner (or, for `transfer`, the token holder).
|
|
6
6
|
|
|
7
|
-
**Check state before and after any op with `abx state <addr>`** — a read-only, on-chain snapshot (no tx, no local index): owner · supply (minted / max, nextTokenId) · paused · minter · primary payee · royalty · renderer. It's the fast way to confirm an op landed (e.g. after `mint`/`unpause`), and to see who can currently mint — prefer it over piecing state together from per-token `abx tokenuri` calls. Works for a Series or a 1/1.
|
|
7
|
+
**Check state before and after any op with `abx state <addr>`** — a read-only, on-chain snapshot (no tx, no local index): owner · supply (minted / max, nextTokenId) · paused · minter · primary payee · royalty · royalty cap · burnable · renderer. It's the fast way to confirm an op landed (e.g. after `mint`/`unpause`), and to see who can currently mint — prefer it over piecing state together from per-token `abx tokenuri` calls. Works for a Series or a 1/1.
|
|
8
8
|
|
|
9
9
|
| Command | Does | Decision to surface |
|
|
10
10
|
|---|---|---|
|
|
11
11
|
| `abx mint <addr> [--to 0x.. (default owner)]` | issue a token — the 1/1's token #0, or a Series' next-in-order token | who receives it (owner = pre-mint; a buyer = primary sale) |
|
|
12
|
+
| `abx submit-app <collection> --name … --summary … --description …` | list that collection in the ABX App Store (mint + catalog copy). Optional; not part of deploy. Re-run writes params only if already claimed | confirm store copy (not the collection name); they must own the collection |
|
|
12
13
|
| `abx refresh <addr> [--token 0]` | ask marketplaces to (re)index the token's metadata | none — run it right after a mint or URI change |
|
|
13
14
|
| `abx transfer <addr> --to 0x.. [--token 0]` | settle a sale/gift — move the token to a new holder | who the recipient is (price/terms are off-chain) |
|
|
14
15
|
| `abx set-token-uri <addr> --uri <base>` | re-point the resolver **base** (e.g. move the node); contract derives `{base}/{chainId}/{address}/{tokenId}` | the new base URL |
|
|
@@ -16,23 +17,45 @@ After launch the owner operates the project. Each command builds a tx, signs it
|
|
|
16
17
|
| `abx set-contract-uri <addr> (--uri <base> \| --override <uri>)` | re-point the collection base, or pin a fixed locator | the new base / locator |
|
|
17
18
|
| `abx deploy-resolver --provider <fly\|render\|vps> [--domain <host>]` | scaffold a hosted read-only resolver (the default off-chain path); generates the admin token | which provider; the custom domain |
|
|
18
19
|
| `abx deploy-effects --resolver-url <resolver>` | scaffold the render runner (Playwright) beside a hosted resolver — renders code-token stills off-chain and publishes them | the storage home (`ABX_STORAGE_BACKEND`: ipfs/arweave/s3, not local `fs`) |
|
|
19
|
-
| `abx render <addr> [id…] [--remote <
|
|
20
|
-
| `abx
|
|
21
|
-
| `abx
|
|
20
|
+
| `abx render <addr> [id…] [--remote <name\|url>]` | render missing stills/traits now (repair lane); `--remote` publishes to a REMOTE resolver | none — idempotent; local captures need Playwright chromium |
|
|
21
|
+
| `abx remote [<name\|url>]` | inspect a remote service: named remotes in `.env`, or a target's descriptor (chains · managed rendering · signup URL) + the projects your token sees | none — read-only; the "is my provider key valid?" check |
|
|
22
|
+
| `abx add <addr> --remote <name\|url>` · `abx index <addr> --remote` · `abx forget <addr> --remote` | register / re-index (nudge) / deregister a contract on a REMOTE resolver (control plane) | which remote — a name (`ABX_REMOTE_<NAME>_URL/_TOKEN`, a managed provider's key) or bare `--remote` (self-host default: `ABX_PUBLIC_BASE_URL` + `ABX_REMOTE_SELF_TOKEN`); ad-hoc `--remote <url> --remote-token <t>` |
|
|
23
|
+
| `abx migrate <addr> --from <src name\|url> --to <dest name\|url>` | move a contract's **off-chain state** to a new resolver instance (see below) | confirm the cutover step (DNS vs base URI); re-pin any source-only images; only the DESTINATION needs a token |
|
|
22
24
|
| `abx set-royalty <addr> --bps <0-10000> [--receiver 0x..]` | change the royalty (receiver defaults to current) | the rate, and whether the payee changes |
|
|
25
|
+
| `abx set-royalty-cap <addr> --cap <0-10000>` | **lower** the royalty ceiling (owner-only, reduce-only) — the cap fixed at deploy can only come DOWN | confirm — **one-way**: it never rises, and is refused if it would drop below the live royalty rate |
|
|
23
26
|
| `abx attach <addr> <key> <ipfs://…\|ar://…\|https://…>` | attach a named file to a token → the `artifacts` manifest (the data plane) | the key (how it appears); locator vs `--file` on-chain |
|
|
24
27
|
| `abx set-field <addr> --field <name> (--text "…" \| --value 0x..) [--representation <r>] [--collection]` | set an on-chain metadata field (token or collection scope) — the low-level primitive | which field, where it lives, on-chain vs off |
|
|
28
|
+
| `abx set-gateway <addr> [--ipfs <prefix>] [--arweave <prefix>]` | repoint where this collection's `ipfs`/`arweave` fields are SERVED from (`none` clears → public default) | the prefix (must be the whole thing, e.g. `https://you.mypinata.cloud/ipfs/`) |
|
|
25
29
|
| `abx lock-field <addr> --field <name> [--collection]` | **freeze a field forever** (all its representations) | confirm — irreversible |
|
|
30
|
+
| `abx lock-script <addr>` | **freeze the on-chain program (script chunks) forever** — the lock that freezes a code project's *work* (`setScriptChunk`/`removeLastScriptChunk` revert after) | confirm — irreversible; SeriesCode/EditionCode only |
|
|
31
|
+
|
|
32
|
+
**There is no `abx replace-script`.** Until `lock-script`, the contract still accepts `setScriptChunk`, but the supported CLI does not expose post-deploy rewrite (only `deploy-code --resume` for an *incomplete* setup). To change a live program, deploy a new contract. Don't hand-roll ABI writes.
|
|
33
|
+
|
|
34
|
+
| `abx lock-dependencies <addr>` | **freeze the dependency set forever** (library list + registry pointer) | confirm — irreversible |
|
|
35
|
+
| `abx lock-param-hooks <addr>` | **freeze the three param hooks forever** (configure/augment/transfer) — the lock a *buyer* cares about: the transfer hook is a veto over transfers **and** mints, so until this is sent the owner can arm one | confirm — irreversible; SeriesCode/EditionCode only. Freezing an EMPTY set is the proof "no transfer veto can ever be armed"; a hook already set keeps its veto. No carve-out: hooks are owner-only forever and a freeze holds against everyone, including on an **ownerless** project — unlike the 721C validator, don't conflate them ([why](code-projects.md#live-data-the-augment-hook--the-hook-is-the-setting)) |
|
|
36
|
+
| `abx set-seed-source <addr> <0x..\|canonical\|none>` | re-point a **code project's** mint-seed source — the creator's own `IAbxSeedSource` (commit-reveal / VRF), the shared `canonical` one, or `none`. Probed first: it must answer `seed(uint256,address)` with 32 bytes or the command refuses | **future mints only** (assigned seeds are settled) — on a part-sold drop the collection then spans two sources; pause and say so. Code projects only |
|
|
26
37
|
| `abx set-admin <addr> --to 0x..` | hand over contract ownership | confirm the new admin address |
|
|
27
38
|
|
|
28
|
-
**`lock-field` / `lock-uri` are permanent — always confirm with the human first.**
|
|
39
|
+
**`lock-field` / `lock-uri` / `lock-script` / `lock-dependencies` / `lock-param-hooks` are permanent — always confirm with the human first.**
|
|
29
40
|
|
|
30
|
-
|
|
41
|
+
**A code project isn't frozen until its PROGRAM is.** `lock-field`/`lock-uri` freeze *metadata*; they do **not** touch the script chunks. For a generative/code drop (`SeriesCode`/`EditionCode`) the work lives in the on-chain program, and the owner can keep rewriting it (`setScriptChunk`/`removeLastScriptChunk`) until you run `abx lock-script <addr>`. The full set is **`lock-script` + `lock-dependencies` + `lock-field`/`lock-uri`** (and `set-schema … lock=now` for any param whose value should freeze too). `abx verify` reports the script/dependency lock state so you can see what's still mutable.
|
|
42
|
+
|
|
43
|
+
**Even with every lock engaged, don't tell a creator the OUTPUT is immutable.** A lock freezes what this contract *stores*, and the metadata locks reach fields and the URI config — not the param store. **An *ungoverned* param has no lock**, and the renderer serves it into `tokenURI` — so an owner can still change the served metadata and the render. (A *schema'd* param is different: it can be welded permanently — see "Welding a PARAM" below — but a welded value can still be *overridden at read time* by an `--augment` hook until `lock-param-hooks` freezes the hook set.) And a `name@version` **`--dep` resolves live from the dependency registry** on every read: `lock-dependencies` pins which library the ref means, not the bytes the registry hands back. `abx verify`'s `chain-complete` is about *where* bytes come from, not whether they change. Say **"the metadata is locked"**; the stronger claim needs on-chain `--dep 0x…` refs plus locked fields. **And a lock a buyer will ask about that isn't in the metadata set at all: `lock-param-hooks`** — a code project's transfer hook is a *veto* (its revert fails a transfer, and a mint), so an unfrozen hook set is a standing power over whether a collector can sell ([hooks](code-projects.md#live-data-the-augment-hook--the-hook-is-the-setting)). Read the other way it's a capability — a piece that adapts to live chain state is a real design, not a defect. Full disclosure list (what an owner can and cannot do, and the read for each) → https://abx.docs.artblocks.io/protocol/owner-powers/
|
|
44
|
+
|
|
45
|
+
**Welding a PARAM is a different verb — and it IS a real weld.** Fields and [PostParams](code-projects.md#postparam-schema--the-type--auth-catalog) are separate namespaces that can share a name, and `lock-field` freezes the *field*. To make a param permanent, lock its schema: `abx set-schema <addr> --schema <key>:<Type>:<Auth>:lock=now` (or `abx retire-param <addr> <key>`, the one-arg form). Once that deadline is past, three things hold **forever**, all enforced on-chain:
|
|
31
46
|
|
|
32
|
-
|
|
47
|
+
- every `configure-param` on the key reverts `ParamLockExpired`;
|
|
48
|
+
- **the SCHEMA freezes too** — `set-schema` on the key also reverts `ParamLockExpired`, so its Type, Auth, bounds and `Select` option table can never be edited. (That matters concretely: without it, a locked `Select` param's options could be swapped afterwards and a collector's "Ember" would re-render as "Frost" with no param write at all.)
|
|
49
|
+
- the owner's raw setter was already closed on any schema'd key (`SchemaGoverned`), so there is no back door.
|
|
50
|
+
|
|
51
|
+
`lock=` is **monotonic**: a later `set-schema` may only move the deadline *earlier*, never later and never back to open (`ParamLockNotExtendable`). So the weld cannot be un-welded, and telling a creator "this is permanent" is accurate. (`lock-field` refuses a declared param key rather than letting you lock the wrong thing.) **Two honest caveats to keep, both worth saying out loud before a creator advertises a frozen value:**
|
|
52
|
+
|
|
53
|
+
- a weld freezes the *stored* value and schema — an `--augment` hook can still override what a reader is served, until `lock-param-hooks` freezes the hook set (and that lock freezes the hook **addresses**, not the code behind them — an upgradeable proxy at a locked address can still change behavior, so permanence needs immutable hook contracts);
|
|
54
|
+
- a weld covers **token-scope** writes; a **contract-scope default** on the same key can still be cleared. `clearContractParam` deliberately sits outside the schema guard — it is the only recovery path from a value poisoned before the schema existed — so an owner may set a collection-wide default, weld the key, sell tokens that inherit it, and then delete the default, changing every token that never wrote its own value. Clearing can only *remove* a fallback (it can't forge a value, bypass an auth rule, or touch a token-scope value already written). **If a collection-wide value must be permanent, write it per token through the governed path** instead of leaving it as an inherited default.
|
|
55
|
+
|
|
56
|
+
### Authorship + rights (credit + license)
|
|
33
57
|
|
|
34
|
-
|
|
35
|
-
- **Later / to change**: `abx set-field <addr> --collection --field artist --text "Casey Reas"`.
|
|
58
|
+
The reserved **collection** keys are `creator`, `display_notes`, `creator_links`, `license` (a rights *identifier* like `CC0-1.0` / `CC-BY-NC-4.0`). `--creator`/`--license`/`--display-notes`/`--creator-links` on any deploy command are **sugar over `set-field`** — they pre-fill the same on-chain inline collection fields at deploy time (parity with `--description`/`--external-url`); change them later with `abx set-field <addr> --collection --field creator --text "Casey Reas"` directly.
|
|
36
59
|
|
|
37
60
|
They project into `contractURI` on both the resolver and the on-chain renderer; unset ⇒ omitted. Works on **any** token type (1/1 · Series · code) — reserved keys in the shared metadata store, not a code-only feature. Being *reserved*, they never become `artifacts` entries the way an arbitrary `attach` key would.
|
|
38
61
|
|
|
@@ -46,9 +69,15 @@ A token anchors **named, typed files**; the served JSON's **`artifacts`** array
|
|
|
46
69
|
- **`mimeType` is declared from the URL extension** (`…/master.tiff` → `image/tiff`, `…/coa.pdf` → `application/pdf`). No extension → `application/octet-stream` (a warning fires). **Point the URI at the file itself with its real extension** — the on-chain field has no MIME slot; the extension is the declaration.
|
|
47
70
|
- **`--file <path>`** stores tiny bytes ON-CHAIN (SSTORE2) instead — for a small file with no external host (≈200 gas/byte, so locators are the norm for anything non-trivial).
|
|
48
71
|
- scope: `--token <id>` (default 0) or `--collection`; any lane; `--dry-run` previews the tx.
|
|
72
|
+
**Gateway ≠ field.** An `ipfs`/`arweave` field stores the bare CID/txid — identity — and the HTTPS prefix is a separate collection-wide setting. So "the gateway is slow / my Pinata plan lapsed / I bought a dedicated gateway" is **`abx set-gateway`**: one tx, moves every token, no re-upload, and it works on fields that are already **locked**. Do NOT reach for `set-field` here — it refuses the two gateway keys by name and points at `set-gateway`, because a token-scope write, a wrong representation, or a prefix missing its trailing path would each fail silently. Reserve `set-field` for the case where the CONTENT moved (a new CID, a new backend).
|
|
73
|
+
|
|
74
|
+
- **The complete listing is a resolver surface; PostParams are not.** A resolver serves the full `artifacts` list at `/t/<chainId>/<addr>/<id>`, and `/data/<key>` fetches each file. The bare on-chain `tokenURI` enumerates **reserved fields only** — the EVM cannot enumerate arbitrary FIELD keys — so a project that must surface extra files to consumers today runs a resolver (attached files are stored + keccak-anchored regardless). The params store, by contrast, enumerates its own keys on chain, so any RPC reads a token's whole configuration straight from the contract. One line for a creator: *attachments always need a resolver; params never do.*
|
|
75
|
+
- **Effect outputs are artifacts too.** A code project's runner publishes `render/image`, `render/traits`, and any extra declared output into the same manifest automatically, at the current settled state — files appear as tokens mint and params change.
|
|
76
|
+
- **Set expectations honestly, up front.** No mainstream marketplace (OpenSea/Blur) shows a "files" tab **today** — they render `image`/`animation_url` only. Attached files are a durable, anchored part of the token *now*, read by data-plane-aware tools and any resolver; broad marketplace display is future adoption. So verify an attach with `abx tokenuri <addr> --fetch`, never by refreshing OpenSea.
|
|
77
|
+
- **Not the same as a Series.** `deploy-series` makes N separate tokens, one file each; the data plane is how **one** token holds several named files.
|
|
49
78
|
- **`artifacts` and `abx_provenance` are COMPUTED** — the resolver/renderer assembles them from your fields. `set-field`/`attach` refuse them: you never set the manifest, you attach the files it lists.
|
|
50
|
-
- **Verify** —
|
|
51
|
-
- **On-chain vs resolver reach.** The complete listing (arbitrary attached keys) is a **resolver** surface — the EVM can't enumerate an open set of
|
|
79
|
+
- **Verify** — `abx tokenuri <addr> --fetch` follows the token's own URI and shows the served document, `artifacts` array included; that is the check after an attach, not a hand-built URL. (The route each file is fetched from is `/t/<chainId>/<addr>/<id>/data/<key>` on the resolver, which 302-redirects to the locator — worth knowing, not worth typing.) The attached bytes are stored on-chain + keccak-anchored either way.
|
|
80
|
+
- **On-chain vs resolver reach.** The complete listing (arbitrary attached keys) is a **resolver** surface — the EVM can't enumerate an open set of FIELD keys, so the bare on-chain `tokenURI` (on-chain renderer) emits **reserved fields only**. Attaching is durable + anchored regardless, but to *surface* extra files to consumers today, the project is served by a resolver. **PostParams are the exception**: the params store enumerates its own keys on-chain (`paramSchemaKeys` · `contractParamKeys` · `tokenParamKeys`, then `tokenParam`/`contractParam`), so anyone with an RPC reads the complete configuration **directly from the contract** — no resolver, no indexer, and no metadata document in the middle. That store is the canonical surface, which is why params are *not* also projected into `tokenURI`; read them with `abx state` (schemas) and `abx tokens` (values). Say it to a creator as one line — *attachments always need a resolver; params never do.*
|
|
52
81
|
|
|
53
82
|
### Series-only owner ops
|
|
54
83
|
|
|
@@ -63,13 +92,33 @@ For a multi-token Series ([SKILL → Series](../SKILL.md#series-multi-token-drop
|
|
|
63
92
|
| `abx set-primary-payee <addr> --payee 0x..\|none` | declare where primary-sale proceeds go (`none` clears) | the payout destination (a wallet or splitter) |
|
|
64
93
|
| `abx set-max-invocations <addr> --max <N>` | **lower** the supply cap (monotonic) — e.g. close an open edition early at the minted count | confirm — can only decrease, never below what's minted |
|
|
65
94
|
|
|
66
|
-
`set-field` / `lock-field` work per **token** for a Series — pass the token id (`--field image --file … ` targets that token's
|
|
95
|
+
`set-field` / `lock-field` work per **token** for a Series — pass the token id (`--field image --file … ` targets that token's work); tokens share the collection scope with `--collection`.
|
|
96
|
+
|
|
97
|
+
### Edition owner ops
|
|
98
|
+
|
|
99
|
+
For an **ERC-1155 edition** (`OneOfOneEdition` / `EditionImage` / `EditionCode`, reached via `--copies` at deploy — [SKILL → which command](../SKILL.md#which-command--what-are-you-launching)). Everything above still applies per work (transfer, royalty, fields, locking); these are the edition-only twins, keyed by **id** instead of a single owner.
|
|
100
|
+
|
|
101
|
+
| Command | Does | Decision to surface |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| `abx mint <addr> --token-id <n> --amount <n>` | mint **copies** of one id — `--token-id` defaults to `0` on a 1/1-edition (its only id), **required** on EditionImage/EditionCode (ids are caller-named works; see `abx tokens <addr>`) | which id, how many copies, who receives them |
|
|
104
|
+
| `abx transfer <addr> --amount <n> --from 0x.. --to 0x..` | move copies of an id between holders — `--from` is **required** (an id can have many concurrent holders, so there's no single on-chain "owner" to default from) | which holder is selling, how many copies |
|
|
105
|
+
| `abx set-max-supply <addr> --token-id <n> --cap <n>` | **lower** one id's ERC-1155 supply cap — the per-id twin of `set-max-invocations`; monotonic (never below that id's live supply) | confirm — **one-way**: `--cap open` is refused (once capped, an id can never re-open), so closing an id is final |
|
|
106
|
+
| `abx ping-uri <addr> --token-ids <csv\|range>` | **owner-only**: re-emit the per-id refresh events for the given ids, after a contract-wide re-point (`set-token-uri`/`set-renderer`) that only emitted its own config event, not a per-id one. Always emits ERC-4906 `MetadataUpdate(id)`; also emits the native ERC-1155 `URI(uri(id), id)` **unless an on-chain renderer is configured** (that event would carry the whole rendered document, at 8 gas per byte of log data) | sign as the owner — run it when a marketplace/indexer only honors the per-id event and missed the re-point |
|
|
107
|
+
| `abx minter configure <addr> --token-id <n> --price <eth> --allocation <n>` | price **one id** on the edition sibling minter (`AbxFixedPriceMinter1155`) — every id sells on its own terms, not one price for the whole contract | which id, price, allocation |
|
|
108
|
+
| `abx minter buy <addr> --token-id <n> --quantity <n>` | buy `n` copies of that id in one purchase — pays `price × quantity` | how many copies |
|
|
109
|
+
| `abx tokens <addr> --holder 0x..` | **read** (no tx): adds a `held` column — `balanceOf(holder, id)` per id, straight from the contract. The ONLY way to ask what one address holds: `supply/maxSupply` says how many copies exist, never who has them (an id has many concurrent holders and the set isn't enumerable from a head read) | none — this is the "did my transfer actually land?" read |
|
|
110
|
+
|
|
111
|
+
`--token-id` is required on every edition op above that names a specific work, except a 1/1-edition (defaults to `0`, its only id) — and refused on a 721 target either way. Passing `--count` to `mint`, or omitting `--from` on `transfer`, is refused on an edition, pointed at the flag to use instead.
|
|
112
|
+
|
|
113
|
+
**Two numbers, not one — get this straight with the creator BEFORE deploying.** An edition has an **id space** (how many distinct works) and a **per-id copy cap** (`--copies`). They multiply. On `deploy-code --copies 100` the id space comes from `--max`, which **defaults to 16** — so "100 copies" without `--max` means *up to 16 different generative variations × 100 copies each*, i.e. as many as 1600 tokens, not 100. A creator who says "an edition of 100" almost always means **one** work: pass `--max 1 --copies 100`. The dry-run states the real shape (`up to N id(s), M copies each`) — read that line back to them before any spend. Same arithmetic on `deploy-series --copies` (ids come from the folder) and `deploy --copies` (a single id, so `--copies` IS the total).
|
|
67
114
|
|
|
68
115
|
### Selling — the shared fixed-price minter
|
|
69
116
|
|
|
70
117
|
Instead of writing a sale contract, use the canonical **`AbxFixedPriceMinter`**: one **ownerless, multi-tenant singleton** (shared like the factory/renderer) that sells *any* Series exposing the sequential mint primitive. Sale config is keyed by token address and **all authority defers to the token owner** — so the minter holds no admin, custodies nothing, and routes proceeds to the token's own `primaryPayee()` (read fresh each sale). Design detail: [Minter spine](../../../../specs/protocol/minter-spine.md).
|
|
71
118
|
|
|
72
119
|
> **The minter sells a Series, mint-on-purchase — not a 1/1 you already hold.** It mints *unminted* supply to a buyer, so it needs the Series interface (`minter`/`paused`/`primaryPayee`). A plain `abx deploy` **1/1 has none of that** — its only post-mint move is `abx transfer` (settle an off-chain sale). So there are two real paths for selling one piece: **(A)** mint the 1/1 to yourself, sell it off-platform (OpenSea/Blur), then `abx transfer --to <buyer>`; or **(B)** for a native fixed-price *primary* sale, deploy the single piece as a **1-token Series** (`abx deploy-series` on a one-file folder, `--no-mint`) and run the go-live sequence below with `--allocation 1`. Don't try to wire the minter to a plain 1/1 — `set-minter`/`unpause` will revert.
|
|
120
|
+
>
|
|
121
|
+
> **The edition minter is the one exception — it DOES sell a "1/1"-shaped work directly.** `AbxFixedPriceMinter1155` targets `IAbxEditionMint`, which `OneOfOneEdition` implements (id space fixed to `0`), so a priced open/limited edition of a single work needs no Series wrapper: `abx deploy --copies <n|open> --no-mint` ships the full sale stack on its own — run the same go-live sequence below with `--token-id 0`.
|
|
73
122
|
|
|
74
123
|
| Command | Does | Notes |
|
|
75
124
|
|---|---|---|
|
|
@@ -86,6 +135,30 @@ abx unpause <token> # open the sale (th
|
|
|
86
135
|
```
|
|
87
136
|
Two independent grants — *configured* (on the minter) and *assigned* (on the token) — so "not configured" and "not assigned" are distinct, diagnosable reverts; `abx minter show` flags whichever is missing. **One mint per `purchase()`**; a buyer who wants several sends several txs. Allocation and the token's `maxInvocations` both bind (tighter wins) — keep reserves by allocating less than the supply. Other mechanics (auctions, allowlists) are *different* minters, not knobs here.
|
|
88
137
|
|
|
138
|
+
> **Re-pricing a live sale fails the buys already in flight — by design.** Every purchase carries the terms it was quoted (payment token + a max price) and reverts `SaleTermsChanged` if the sale moved; that's what keeps a re-`configure` from reaching a buyer's ERC-20 allowance. So a mid-mint price change isn't silent: some in-flight buys revert and those buyers just retry (the CLI and the scaffolded mint page re-read the terms every time). Want a clean cutover? `abx pause <token>` → `minter configure …` → `abx unpause <token>`.
|
|
139
|
+
|
|
140
|
+
**Reserves and airdrops — the two recipes creators actually ask for.** Both are plain `mint` calls;
|
|
141
|
+
neither needs a minter. Do not invent a flag for them.
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
abx mint <token> --to 0xALICE # airdrop: one call per recipient, N recipients ⇒ N calls
|
|
145
|
+
abx mint <token> --to 0xBOB
|
|
146
|
+
abx mint <token> --count 5 # reserve: 5 tokens to yourself (Series mints in order)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The reserve decision is a **choice between two options — present both, don't pick silently**:
|
|
150
|
+
|
|
151
|
+
1. **Pre-mint the reserve** (`abx mint --count k` before `unpause`) — the k pieces are yours, off the
|
|
152
|
+
market, provably held. The sale then sells whatever is left. Simplest to explain to a buyer.
|
|
153
|
+
2. **Under-allocate the minter** (`--allocation <supply − k>`) — nothing is minted up front, and the
|
|
154
|
+
last k stay unminted until you decide. Keeps your options open; costs nothing until you use it.
|
|
155
|
+
|
|
156
|
+
They compose (pre-mint some, under-allocate the rest), and both bind against `maxInvocations` — the
|
|
157
|
+
tighter of allocation and supply always wins. An airdrop after the sale opens is fine too: owner
|
|
158
|
+
minting is not gated by `paused` (pausing blocks *minter* sales, not the owner).
|
|
159
|
+
|
|
160
|
+
**On an edition, only the minter calls need `--token-id`** — `minter configure/show/buy --token-id <n>` (a different sale per work, on `AbxFixedPriceMinter1155`); `set-minter`/`set-primary-payee`/`pause`/`unpause` stay exactly as above, contract-wide (one minter, one payee, one pause gate for the whole id space). `minter buy --token-id <n> --quantity <n>` buys that many copies in one purchase, paying `price × quantity`.
|
|
161
|
+
|
|
89
162
|
### A mint website for buyers — `abx mint-page`
|
|
90
163
|
|
|
91
164
|
Once a sale is live, `abx mint-page <token>` scaffolds a **self-contained Next.js mint site** for the collection — a paginated gallery of minted tokens, connect-wallet, and a mint button — prefilled with the token, the shared minter, and the chain. It's the sale-side sibling of `abx deploy-resolver`: an artifact the creator **owns and customizes**, not a service we run.
|
|
@@ -96,6 +169,7 @@ Once a sale is live, `abx mint-page <token>` scaffolds a **self-contained Next.j
|
|
|
96
169
|
| Runs | `cd mint-page && npm install && npm run dev` (preview) → **Vercel** (`npm i -g vercel && vercel --prod`), setting the `NEXT_PUBLIC_*` vars in the dashboard |
|
|
97
170
|
|
|
98
171
|
- **Backend-free** — reads sale state + each token's image straight from chain (`tokenURI(id)`); mints via the buyer's injected wallet (viem, no API keys). On-chain collections need nothing else; off-chain ones rely on their `tokenURI` resolving.
|
|
172
|
+
- **Works against an edition too** — pointed at `OneOfOneEdition`/`EditionImage`/`EditionCode`, the page is a **token-id-and-quantity purchase card** instead of a gallery (no minted-tokens list in this v1 page). Refuses only a plain 1/1 (no minter lane) — deploy a one-token Series, or an edition of one work, instead.
|
|
99
173
|
- **A starting point** — plain React + one CSS file. Offer it as a release next-step, then help build a bespoke page per drop. The README covers multi-wallet (RainbowKit), ERC-20 (add an `approve` step), and the RPC/gallery notes. V1 handles **ETH** sales.
|
|
100
174
|
- **RPC safety** — the embedded read RPC is public/keyless (it ships to the browser). Never bake a secret-keyed endpoint into a `NEXT_PUBLIC_` var.
|
|
101
175
|
|
|
@@ -103,6 +177,7 @@ Once a sale is live, `abx mint-page <token>` scaffolds a **self-contained Next.j
|
|
|
103
177
|
|
|
104
178
|
- **Same resolver, new address** (moved the *node*, kept its projection store — e.g. a VPS restored from the same volume): `set-token-uri` + `set-contract-uri` to the new **base**, then `abx index`. With a custom domain, just re-point DNS — no tx.
|
|
105
179
|
- **A fresh resolver instance** (new host, empty store): run **`abx migrate <addr> --from <old-url> --to <new-url>`** first. The new resolver replays on-chain state itself; `migrate` bridges what it can't derive — off-chain `description`/`external_url`, off-chain traits, image **content locators** — by reading the old resolver's public API (they never talk directly; provenance makes the JSON self-describing). It verifies parity and **does not cut over**.
|
|
180
|
+
- **Leaving (or joining) a managed provider — the same two cases.** `abx migrate <addr> --from <provider name|url> --to <your-node-or-new-provider>` reads only the provider's **public** endpoints — the interface guarantees the exit needs zero provider cooperation, and no source credential. Then the standard cutover below (a provider base URL is the "provider endpoint" case: one on-chain re-point). Registration is never load-bearing for resolution, so a vanished provider costs availability, never state.
|
|
106
181
|
|
|
107
182
|
### The cutover, after a clean `migrate`
|
|
108
183
|
|
|
@@ -114,3 +189,32 @@ New resolver now serves byte-identical metadata, but traffic still hits the old
|
|
|
114
189
|
### Source-only ("node-custody") images — `migrate` handles these automatically
|
|
115
190
|
|
|
116
191
|
An image committed by hash but never pinned to a durable backend serves from the source's own `/image` route — no portable URL, so copying it points the new host back at the dying old one. `migrate` instead fetches the bytes, re-hashes them against the on-chain commitment (trustless), re-pins them to **your** durable backend, and bridges the new locator. The one case it can't fix: a migrating machine with no durable backend (only `fs`/`cloud`) — it bridges nothing source-bound and tells you to re-run with `--backend ipfs|arweave`. So configure a durable backend before migrating a project that used local-disk custody.
|
|
192
|
+
|
|
193
|
+
## Listing in the ABX App Store — `abx submit-app`
|
|
194
|
+
|
|
195
|
+
**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.
|
|
196
|
+
|
|
197
|
+
1. **Draft store copy with the creator.** `--name` / `--summary` / `--description` here are what someone can *do*, not the collection's NFT metadata. Do not silently copy deploy `--name` / `--description`. Confirm category, stage, launch URL, and icon mark/tone.
|
|
198
|
+
2. **You run the command** (same signing lane as the deploy). `--dry-run` first, then send:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
abx submit-app <collection> \
|
|
202
|
+
--name "…" --summary "…" --description "…" \
|
|
203
|
+
--category Create --stage Prototype \
|
|
204
|
+
--url https://… \
|
|
205
|
+
--sign
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
3. They sign once to mint the listing, then a few times to write metadata (the chain caps how much fits in one tx). If mint already succeeded, re-running skips the mint and only writes params.
|
|
209
|
+
4. Humans can instead use the store's `/submit` page; same txs. Don't hand them a list of `configure-param` commands.
|
|
210
|
+
|
|
211
|
+
The wallet must **own the collection** at mint (the gate checks `isAbxClone` + `owner()`). After that, control follows the **entry token**.
|
|
212
|
+
|
|
213
|
+
## Deploy strategy — the mechanics
|
|
214
|
+
|
|
215
|
+
- **`abx predict [--salt 0x..] [--for 0x..]`** pre-computes the address. No `--salt` → reserves a fresh, front-run-proof salt to the deployer; pass `--salt` for a vanity/known address. The salt's leading 20 bytes are an access guard: zero ⇒ anyone may deploy; non-zero ⇒ only that signer.
|
|
216
|
+
- **`--dry-run` without `--salt` prints NO address** — enforced, not a trap to remember: without a pinned salt there's nothing real to quote, so it prints only the freshly-reserved salt plus a ready-to-paste `--salt …` re-run (or `abx predict --salt … --for …`). Pin that salt and re-run to see the real, reproducible address before you present anything.
|
|
217
|
+
- **`abx refresh <addr>`** asks marketplaces to re-index. **Both lanes emit ERC-4906** on metadata/URI changes so 4906-aware marketplaces self-refresh, and `refresh` is the fallback (+ genesis mint). **An edition additionally has ERC-1155's native `URI` event, which is NOT emitted on a contract-wide re-point** (no range form exists) — so after `set-token-uri`/`set-renderer` on an edition also run `abx ping-uri <addr> --token-ids <ids>` for consumers that honor only that event. With `OPENSEA_API_KEY` it calls OpenSea directly; else it prints the links to click.
|
|
218
|
+
- **There is no "add it to a marketplace" step — don't offer one.** Marketplaces discover the collection from chain (the mint, plus ERC-4906 on either lane); `abx refresh` — and `abx ping-uri` on an edition — is the only nudge. Suggesting a manual listing implies work that doesn't exist. **The ABX App Store is different** — that registry is an explicit opt-in (`abx submit-app`), not marketplace discovery. Offer it after deploy; never fold it into deploy. See [List in the App Store](../SKILL.md#list-in-the-abx-app-store-optional).
|
|
219
|
+
- A `--no-mint` deploy reconstructs + serves normally (dashboard shows #0 "not yet minted"; metadata/image already resolve).
|
|
220
|
+
- **Etherscan source is already verified** — your deploys are EIP-1167 clones Etherscan auto-recognizes as proxies of the verified implementation.
|
package/skill/reference/setup.md
CHANGED
|
@@ -1,7 +1,50 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Setup — install `abx`, configure `.env`, then RPC & troubleshooting
|
|
2
2
|
|
|
3
3
|
[← back to SKILL.md](../SKILL.md)
|
|
4
4
|
|
|
5
|
+
**First time? Start at [Finding, installing and configuring `abx`](#finding-installing-and-configuring-abx)** —
|
|
6
|
+
that section is the required setup. The RPC and troubleshooting material below it is reference you reach
|
|
7
|
+
for when indexing is slow or a resolver will not serve, not something a first launch needs.
|
|
8
|
+
|
|
9
|
+
## Finding, installing and configuring `abx`
|
|
10
|
+
|
|
11
|
+
`abx` needs **Node ≥ 22.13** at runtime (the projection store uses built-in SQLite, unflagged only from
|
|
12
|
+
v22.13.0 / v23.4.0). On anything older the CLI refuses to start and names the fix — do not work around it
|
|
13
|
+
with `NODE_OPTIONS=--experimental-sqlite`, that combination is untested.
|
|
14
|
+
|
|
15
|
+
### Find `abx` before you install it — local first, then global
|
|
16
|
+
|
|
17
|
+
**Resolve in this order and use the first hit.** Don't jump to a global install; a project-local CLI is pinned in the creator's `package.json` (reproducible, and what `abx skill install` version-locks against), so it wins whenever it exists:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
./node_modules/.bin/abx version # 1. project-local — PREFER this (invoke as `npx abx <cmd>` once you know it's there)
|
|
21
|
+
abx version # 2. a global install already on PATH
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- **Probe the local binary by PATH, never with `npx abx`.** The bare `abx` name on npm is an **unrelated squatted package**, so a plain `npx abx` with nothing local *downloads that*. And `--no-install` does **not** make the probe safe or trustworthy: it only skips a *fresh download*, and npm will still happily run any `abx` binary sitting in the npx cache from an earlier run — which can be a stale version (a real sweep saw `--no-install` report a months-old build as the project's CLI) or, if a plain `npx abx` was ever run on that machine, the squatted package itself. `./node_modules/.bin/abx` is unambiguous: it exists in THIS project or it doesn't.
|
|
25
|
+
- In the **abx source repo** (contributor), neither applies: use `pnpm abx <cmd>`, which runs the live `tsx` source.
|
|
26
|
+
|
|
27
|
+
**Nothing found → install.** The package is **`@artblocks/abx-cli`** (not `@artblocks/abx-sdk` — that's the library, and installing it gets you no `abx` binary; a real session lost a cycle to exactly that mistake):
|
|
28
|
+
|
|
29
|
+
| Situation | Install | Then invoke as |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| The creator has a project dir (a `package.json`) — **default** | `npm install --save-dev @artblocks/abx-cli` | `npx abx …` |
|
|
32
|
+
| No project, or they asked for a machine-wide tool | `npm install -g @artblocks/abx-cli` | `abx …` |
|
|
33
|
+
|
|
34
|
+
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.
|
|
35
|
+
|
|
36
|
+
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).
|
|
37
|
+
|
|
38
|
+
`.env` (in the creator's project dir) = **secrets only**:
|
|
39
|
+
- **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.
|
|
40
|
+
- `ABX_RPC_URLS`, `ABX_PUBLIC_BASE_URL` (remote-resolver custody only), optional `OPENSEA_API_KEY`, plus any backend secret.
|
|
41
|
+
- **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).
|
|
42
|
+
|
|
43
|
+
<sub>Working from the abx **source repo** (contributor)? `pnpm install`, then `pnpm abx <cmd>` or `pnpm sandbox`. Every command below is identical.</sub>
|
|
44
|
+
|
|
45
|
+
**Indexing reads the event log via `eth_getLogs` from the contract's deploy block**, which the CLI records at deploy and forwards to a resolver on `add` — so a normal deploy→index scans a small recent window, is fast on any RPC, and re-indexes incrementally. **If indexing is slow, or a hosted resolver won't serve, ASK IT: `abx status <addr> [--remote <name>]`** reports the lifecycle (`queued`→`backfilling`→`live`, plus `stale`/`failed` with a machine-readable cause), so "still catching up" and "broken" stop looking alike. Then **check the scan floor (is it scanning from block 0?) before blaming the RPC tier** — that mis-diagnosis is a known trap. Range caps, pruned log history, and which endpoint to give a resolver under load are covered below.
|
|
46
|
+
|
|
47
|
+
|
|
5
48
|
## Choosing an RPC (drives re-index speed)
|
|
6
49
|
|
|
7
50
|
Reconstruction reads the event log via `eth_getLogs` **from the contract's deploy block** — the CLI records it at deploy and forwards it to a resolver on `add`, and discovers it on-chain (a getCode binary search) for a contract it didn't deploy here. So a normal deploy→index scans a *small, recent* window and is fast on **any** RPC. Providers cap the getLogs block range very differently **and these caps change** — treat any number as illustrative, re-verify before you quote it. *(Illustrative, Jun 2026: Alchemy free ≈10 blocks/call, Infura/Chainstack free ≈2000, PublicNode/dRPC vary; no-key public endpoints are the lowest-friction to try.)* The toolkit **auto-chunks**, so any RPC yields correct state — but a range cap is **not** cost-free to shrug off: a **from-genesis** or long-span reconstruction, or a resolver serving many code projects under load, is slow and rate-limit-prone on a capped endpoint. `abx doctor` rates each endpoint and flags a capped one; treat that flag as a real infra signal, not noise. **First diagnosis when indexing is slow or a hosted resolver won't serve: is it scanning from block 0? Check the scan floor before the RPC tier.**
|
|
@@ -11,26 +54,34 @@ When `abx doctor` flags a tight range, or a reconstruction would be large/slow
|
|
|
11
54
|
2. **Present clear choices with current specifics** — (a) point `ABX_RPC_URLS` at a higher-range endpoint *(recommended for anything large)*, with the exact signup/URL steps you found; or (b) stay on the current RPC and accept the wait.
|
|
12
55
|
3. The CLI **stops a large job with an estimate** instead of grinding through thousands of requests; a better RPC is the real fix. To chunk through anyway, re-run with `--yes` (or raise `ABX_GETLOGS_MAX_CHUNKS`); `ABX_GETLOGS_RANGE` tunes the window.
|
|
13
56
|
|
|
14
|
-
**One RPC var, network-scoped.** `ABX_RPC_URLS` is a comma/space-separated list of endpoints **for the active `ABX_CHAIN`** (there is no singular `ABX_RPC_URL`). The client **fails over** across them at request time — a request one endpoint rejects (a too-wide `eth_getLogs`) is retried on the next — and `abx doctor` **probes every endpoint** and reports a per-endpoint verdict (✓ wide range + archive · ⚠ usable but range-capped · ✗ unusable, with the reason, e.g. *archive refused* / *wrong network*), then names the best for reconstruction. So list a couple, let `doctor` pick, and order the list best-first. For **multiple chains**, set `ABX_RPC_URLS_<CHAIN>` (e.g. `ABX_RPC_URLS_BASE_SEPOLIA`, `ABX_RPC_URLS_SEPOLIA` — the default `ABX_CHAIN` is `base-sepolia`) — a per-chain list that overrides the bare var, never mixed, so one network's endpoints can't leak into another. The var *names* a network but the URL could point anywhere, so before any write the toolkit **verifies `eth_chainId` matches `ABX_CHAIN`** and hard-fails on a mismatch (reads stay unguarded/fast) — that's what makes "which network is this?" a checked fact, not a guess.
|
|
57
|
+
**One RPC var, network-scoped.** `ABX_RPC_URLS` is a comma/space-separated list of endpoints **for the active `ABX_CHAIN`** (there is no singular `ABX_RPC_URL`). The client **fails over** across them at request time — a request one endpoint **rejects** (a too-wide `eth_getLogs`) is retried on the next. Note the limit of that: failover triggers on an *error*, and an endpoint that has **pruned its log history** does not error — it answers `eth_getLogs` with `[]` and HTTP 200, which is a success, so the pool keeps asking it. That is why `abx doctor` **probes every endpoint** and reports a per-endpoint verdict (✓ wide range + archive · ⚠ usable but range-capped · ✗ unusable, with the reason, e.g. *history pruned ~500000 blocks back* / *archive refused* / *wrong network*), then names the best for reconstruction — and why it warns when a **reachable** endpoint can't serve history, since ordering is what decides who answers first. So list a couple, let `doctor` pick, and order the list best-first. For **multiple chains**, set `ABX_RPC_URLS_<CHAIN>` (e.g. `ABX_RPC_URLS_BASE_SEPOLIA`, `ABX_RPC_URLS_SEPOLIA` — the default `ABX_CHAIN` is `base-sepolia`) — a per-chain list that overrides the bare var, never mixed, so one network's endpoints can't leak into another. The var *names* a network but the URL could point anywhere, so before any write the toolkit **verifies `eth_chainId` matches `ABX_CHAIN`** and hard-fails on a mismatch (reads stay unguarded/fast) — that's what makes "which network is this?" a checked fact, not a guess.
|
|
58
|
+
|
|
59
|
+
**Named remotes follow the same env-name normalization**: `--remote my-provider` reads `ABX_REMOTE_MY_PROVIDER_URL` / `ABX_REMOTE_MY_PROVIDER_TOKEN` (uppercase, non-alphanumerics collapse to `_`). Inspect what's configured with `abx remote` ([hosting.md → Managed providers](hosting.md#managed-providers--a-resolver-someone-else-runs---remote-name)).
|
|
15
60
|
|
|
16
61
|
**Re-index is incremental by default** — it resumes from the last-indexed block and only fetches new blocks, so routine re-indexing (after an owner op, or just refreshing) stays instant *regardless* of the range cap. Even the **first** reconstruction scans only from the **deploy block** (not genesis), so range/archive capability bites only on a genuinely long span — a forced full replay (`abx index --full`, a fresh projection, or the dashboard's "Re-index from chain" — the durability proof) of a contract deployed long ago. A "why is this scanning millions of blocks?" moment almost always means the scan floor is wrong (block 0), not that the RPC is inadequate.
|
|
17
62
|
|
|
18
63
|
## Troubleshooting
|
|
19
64
|
|
|
20
|
-
- **doctor: signing key** → **not fatal — a missing key is a fork, not a blocker.** A key in `.env` is only needed to sign *unattended* (hot lane). To sign in a browser wallet instead, run the write with `--sign` and no env key is needed. Offer both: add `
|
|
65
|
+
- **doctor: signing key** → **not fatal — a missing key is a fork, not a blocker.** A key in `.env` is only needed to sign *unattended* (hot lane). To sign in a browser wallet instead, run the write with `--sign` and no env key is needed. Offer both: add `ABX_DEPLOYER_PK` for autonomous signing, **or** use `--sign`. **deployer unfunded** → fund the printed address from a faucet for the active chain (Base Sepolia by default, else Sepolia).
|
|
21
66
|
- **a write reverts with no clear reason (`--onchain-image` / staging)** → don't thrash by retrying variations. Get the actual reason: re-run the same call read-only with `cast call <addr> "<sig>" <args> --rpc-url <one of your ABX_RPC_URLS>`, which surfaces the revert string. The classic culprit is a stale chunk store (deployed before a contract upgrade) — the toolkit now detects this and auto-deploys a current store with a yellow notice, so you shouldn't hit the silent revert, but verify the resolved chunk-store/factory addresses are live (`cast code <addr>`; `abx doctor` / `abx status` print the factory) if anything looks off. **Never silently switch the representation you told the user you'd use** (e.g. fall back from `reader` to `inline`) — if the path changes, say so and why.
|
|
22
67
|
- **storage 'cloud' missing accessKeyId/…** → set `ABX_S3_*` in `.env`. **'ipfs' unreachable** → Kubo not running or `PINATA_JWT` missing.
|
|
23
68
|
- **re-index slow, or a "scan too large" stop** → **first check the scan floor: is it indexing from block 0 instead of the deploy block?** That's the usual cause of a "huge" scan (an `add` that lost the deploy block). The floor is `flags['from-block'] → the stored deploy block → on-chain discovery`; if none resolve, the CLI refuses rather than sweeping genesis. Only once the floor is correct does the RPC's `eth_getLogs` range matter: the toolkit chunks automatically, but a genuinely large job stops with an estimate rather than grinding. Real fix then: research a current higher-range free endpoint and set `ABX_RPC_URLS` (`abx doctor` reports your range); or re-run with `--yes` to chunk through. **archive-range getLogs refused** → use an archive-capable provider.
|
|
69
|
+
- **the scan finished but reconstructed 0 events** (`registered …, but with NO reconstructed state`) → for a project you JUST deployed this is transient (the logs haven't propagated; `abx index <addr> --full` in a minute). For a project that is **not new**, suspect **log retention**: several free endpoints keep only days of history and return an empty success past it, which is indistinguishable from "this contract emitted nothing". The CLI now re-runs the scan against **each configured endpoint on its own** before giving up, and names the one that served the logs — put that one FIRST in `ABX_RPC_URLS_<CHAIN>`. `abx doctor` flags a pruned endpoint up front.
|
|
24
70
|
- **`serve`/resolver returns `{"error":"unknown project"}`** → the resolver you hit doesn't have that contract in *its own* projection store **yet**. Two distinct situations — diagnose which, and NEVER default to "RPC limit":
|
|
25
|
-
- **A
|
|
71
|
+
- **A remote resolver you just registered** (`abx add … --remote`): it may still be **backfilling** — hit `GET /` (or `abx remote <name|url>`, which lists the projects the token sees) to see if it's appearing. If it's slow or stuck, the cause is almost always a **from-genesis scan (from-block=0)**, not the RPC tier — a fixed `abx add` forwards the deploy block, so re-run it and confirm the floor. (Fixed in-toolkit: a first remote add now forwards/derives the deploy block and refuses a genesis default.)
|
|
26
72
|
- **A local `abx serve`**: a **store/port** problem. Usual causes, in order: (1) a **stale/duplicate `abx serve` from an old session** holds the port and serves a *different* store — hit `GET /` and see what it lists; (2) you're serving a different store directory than the deploy indexed into; (3) the contract was never registered there. Fix the server/port/registration.
|
|
27
|
-
- **`/a/…` returns `
|
|
73
|
+
- **`/a/…` returns `503` with "its on-chain code has not been folded into the projection yet"** → exactly what it says: the resolver knows this is a code project (from the deployed extensions) but hasn't read its code yet. **Retry** — this is normal right after a register. It only means trouble if it persists, and then it's the scan-floor bug below.
|
|
74
|
+
- **`/a/…` returns `404 {"error":"no live view — not a code project"}` on a project that IS a code drop** → the resolver indexed *above* the deploy block, so it never saw the deploy at all — a scan-floor bug, **not** a resolver version/compat gap (do NOT redeploy as a static NFT). The 404-vs-503 split is the diagnosis: 404 on a real code drop means the fold never saw this contract's deploy, which is the scan-floor signature. This is the *opposite* of the genesis bug: the floor is too **high**, not too low. It happened when a `--sign` code deploy spanned blocks (deploy at N, mint at N+2) and the mint block was recorded as the floor. Confirm: `GET /api/project/<addr>` → `collectionFields` is `[]` and `fromBlock` sits above the deploy block. Fix: `abx add <addr> --remote <url> --from-block <deployBlock>` — a *changed* floor forces a full replay that picks up the `code` field. Find the true deploy block with `abx add <addr>` locally (it prints "deploy block N (discovered on-chain)"). (Fixed in-toolkit: `deploy-code` now records the clone-CREATION block, and discovers it on-chain rather than trusting the last-tx receipt.)
|
|
28
75
|
- **directory-mode live view 302s to a doubled URL** (`https://arweave.net/https://arweave.net/<txid>/index.html`) → the `code` locator was stored as a full gateway URL and the gateway got prefixed again. Fixed in-toolkit (deploy stores the bare txid/CID; the resolver serves an already-absolute locator verbatim). A resolver image built before the fix still doubles — redeploy it to pick up the resolver-side tolerance.
|
|
29
76
|
- **the thumbnail stays a placeholder on a HOSTED resolver even after `abx render` reports `ran=1`** → the render bytes landed in a store the hosted resolver can't read. A local `abx render` with the default `fs` backend writes to your laptop; `ipfs`/`arweave` write a LOCAL key→CID index the resolver doesn't have. Fix: render **to** the resolver — `abx render <addr> --remote <resolver>` (uploads to your `ABX_STORAGE_BACKEND` home and **publishes** a locator/bytes the resolver serves), or stand up the runner beside it (`abx deploy-effects --resolver-url <resolver>`). Only a SHARED `s3`/`cloud` bucket makes a bare local render visible to a hosted resolver. Confirm with `abx verify <addr>`.
|
|
30
77
|
- **resolver won't start / errors about `ABX_PUBLIC_BASE_URL`** → it refuses a placeholder `.example` base (a scaffold leftover) or a `localhost` base in a hosted image (`ABX_HOSTED=1`), because it bakes that base into every image/animation URL it serves — a bad one serves dead links, so a loud fail beats silent breakage. Set `ABX_PUBLIC_BASE_URL` to the resolver's real public URL (`fly secrets set ABX_PUBLIC_BASE_URL=https://<app>.fly.dev`, or your custom domain). The current scaffold bakes the real platform hostname by default, so this only bites a hand-edited/old artifact or a stripped env.
|
|
31
78
|
- **a hosted resolver LOST its index (or its render thumbnails) after a redeploy** → the SQLite projection wasn't on a persistent volume. The scaffold creates `fly volumes create abx_data` **before** the first deploy so the store lives on `/data` (the mount); if you deployed WITHOUT the volume, the store sat on ephemeral disk and a redeploy wiped it. Fix: ensure the volume exists (`fly volumes list`), then re-index (`abx add <addr> --remote --from-block <deployBlock>`). Note published **render locators + traits live on that same volume** (producer-published, not chain-derived), so restoring them after a volume loss needs a runner re-sweep, not just a chain replay — re-run `abx render <addr> --remote <resolver>` (idempotent; it re-publishes from the storage home without re-rendering) or let the effects service sweep.
|
|
32
79
|
- **the project's on-chain `tokenURI` points at a HOSTED resolver but it isn't serving** → **do NOT "fix" it by running `abx serve` locally and handing over a `localhost` link.** The baked URL is the hosted one; a local serve resolves for no one but you. Fix the HOSTED resolver instead: re-run `abx add <addr> --remote` (now forwards the deploy block), check `GET /` on the host, and vet its RPC with `abx doctor`. A local serve is only ever the answer when the baked base is that same local machine.
|
|
33
80
|
- **port hygiene before `serve`** → check the port is free first (`lsof -i:8787`). If an old session holds it, kill that process or serve this project on a distinct `--port <n>` (and point its `--public-base-url`/tokenURI accordingly) — a stale server silently answering on the port is the #1 cause of "works for me, `unknown project` for everyone."
|
|
34
|
-
- **serving code projects is RPC-heavier than static
|
|
81
|
+
- **serving code projects is RPC-heavier than static content** → the live view rebuilds `tokenData` per request, and template-mode script chunks + data-backed (`String`/`Bytes`) params are read **live per view** (scalar PostParams come from the indexed projection, so they don't hit RPC each view). A resolver serving many code projects under marketplace traffic wants a range-generous, reliable RPC — this is a genuine scaling consideration, distinct from the getLogs range cap above.
|
|
82
|
+
- **a remote command returns 401 unauthorized** → the token the CLI resolved is missing/wrong for that target — the error names the var it used (`ABX_REMOTE_<NAME>_TOKEN` for a named remote, else `ABX_REMOTE_SELF_TOKEN`, else `--remote-token`). Check that var, or rotate the key at the provider if it may have leaked. Named remotes deliberately never fall back to `ABX_REMOTE_SELF_TOKEN` (or the resolver's own server-side `ABX_RESOLVER_ADMIN_TOKEN`).
|
|
83
|
+
- **a remote command returns 403 forbidden** → the key is **valid but not authorized** for this contract/chain — provider-side scoping, not a typo. Don't retry-loop and don't swap tokens blindly: check the provider dashboard, and the descriptor's `chains` (`abx remote <name>`).
|
|
84
|
+
- **register refused with `unsupported_chain`** → the service doesn't serve the project's chain — its descriptor `chains` says which it does. Pick a provider that covers the chain, or self-host.
|
|
85
|
+
- **`/.well-known/abx-service` 404s** → an older self-hosted node (fine if it's yours — the remote commands still work against it once redeployed to the current image) or **not an ABX service at all** — verify the URL with `abx remote <url>` before registering anything; don't register blind.
|
|
35
86
|
- **wallet lane: "no injected wallet"** → the human has no extension wallet; fall back to the hot lane (`--send`) if they're comfortable, or the cold lane for a Safe.
|
|
36
87
|
- **owner op reverts** → the signer isn't the owner/holder. Check `abx status` / the `/api/project` state for the current owner; sign as that wallet.
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[← back to SKILL.md](../SKILL.md)
|
|
4
4
|
|
|
5
|
-
**Diagnose before you act — INSPECT first, don't guess.** The reads: `abx state <addr>` (on-chain owner/supply/paused/minter/renderer), `abx tokenuri <addr> [--token N]` (the actual served/on-chain JSON + `abx_provenance` per field — the fastest "is this really on-chain right now"), `abx verify <addr>` / `abx verify --remote <url>` (does the served image match the chain; is a render real vs a placeholder). Most "it's broken" reports are a stale *marketplace cache*, not a broken token.
|
|
5
|
+
**Diagnose before you act — INSPECT first, don't guess.** The reads: `abx state <addr>` (on-chain owner/supply/paused/minter/renderer), `abx tokenuri <addr> [--token N]` (the actual served/on-chain JSON + `abx_provenance` per field — the fastest "is this really on-chain right now"), `abx verify <addr>` / `abx verify <addr> --remote <name|url>` (does the served image match the chain; is a render real vs a placeholder). Most "it's broken" reports are a stale *marketplace cache*, not a broken token.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**`abx verify` answers two separate questions — read both lines.** *Renders*: is there a current thumbnail, or a placeholder (only relevant to code projects). *Bytes*: do the served bytes still hash to the on-chain commitment — **the one that explains a blank/wrong image**. On `--remote` the byte check runs on the service (it holds both the bytes and the chain), so it needs a credential: without one you'll see **`byte integrity NOT checked`** — that is NOT a pass, so supply the token and re-run before you tell a creator it's fine. A byte MISMATCH exits non-zero and has exactly two causes: durable bytes (`ipfs://`/`ar://`) that were never bridged to the service (`abx add <addr> --remote <name>` forwards the locator), or bytes that only exist on the creator's own machine (`fs` custody) — a hosted resolver can never serve those, so they must be uploaded to a durable backend and re-pointed.
|
|
8
|
+
|
|
9
|
+
Note: `abx state` / `abx tokenuri` / `abx contracturi` are **pure RPC reads** — they work on any address with no local setup. Reach for **`abx contracturi <addr>`** for anything collection-level (ERC-7572: name/description/image of the *collection*, the marketplace banner): it reads `contractURI()` from the contract, follows it, and decodes — so it also proves whether the collection URL the contract commits to is actually being served. **Never hand-build the URL to check that** (a guessed path that 404s reads exactly like an outage; see [SKILL.md → Read first](../SKILL.md#read-first-every-session)). `abx verify` / `abx index` / data-plane ops need the project **registered on this node first** (`abx add <addr>`); their error says so.
|
|
8
10
|
|
|
9
11
|
### Gray placeholder / no image on the marketplace
|
|
10
12
|
Walk the cause down:
|
|
@@ -14,15 +16,50 @@ Walk the cause down:
|
|
|
14
16
|
- Do **not** reach for `abx index --full` or a redeploy for a render/refresh problem.
|
|
15
17
|
|
|
16
18
|
### I changed the metadata but the marketplace shows the old value
|
|
17
|
-
- **First confirm the change landed:** `abx tokenuri <addr> --token N` (or the resolver's JSON) — if it shows the NEW value
|
|
19
|
+
- **First confirm the change landed:** `abx tokenuri <addr> --token N` (or the resolver's JSON) — if it shows the NEW value, the write worked, and that field's `abx_provenance` entry names the route it took (`source` = `inline`/`reader`/`renderer`/`url`/…, with a `note` like *"stored on chain"*). The gap is then 100% the marketplace's cache. (There is no `onChain` boolean in a provenance entry: the contract can't tell whether a value *resolves* on chain without interpreting it. Whether a value is a self-contained `data:` URI or a pointer is visible in the value itself. On a **resolver**, the extra `status` field is the one that answers "was this checked" — `verified`/`mismatch`/`anchored`/`off-chain`.)
|
|
18
20
|
- **Nudge it:** `abx refresh <addr>` — ABX emits **ERC-4906** on metadata changes so 4906-aware marketplaces self-refresh; `refresh` calls OpenSea directly with `OPENSEA_API_KEY`, else prints the link to click. Marketplace caches still lag on their own schedule — that's their side.
|
|
19
|
-
- **
|
|
21
|
+
- **Remote resolver (self-hosted or a managed provider)?** It serves from its own store — a local edit must reach it: `abx add <addr> --remote <name|url>` / `abx index <addr> --remote <name|url>` re-indexes the remote node. Never "resubmit the transaction" or redeploy. If the nudge reported `backfilling`, the service is still catching up — `abx status <addr> --remote <name> --watch` until `live` before blaming the marketplace.
|
|
22
|
+
|
|
23
|
+
### An edition's URI re-point didn't refresh a marketplace/indexer
|
|
24
|
+
**Both lanes emit ERC-4906** — an edition (`OneOfOneEdition`/`EditionImage`/`EditionCode`) advertises
|
|
25
|
+
`0x49064906` and pings `MetadataUpdate(id)` / `BatchMetadataUpdate(0, max)` just like a 721, so a
|
|
26
|
+
4906-aware consumer does see the change. What an edition *also* has is ERC-1155's native
|
|
27
|
+
`URI(string,uint256)`, and that one is **not** emitted everywhere: a contract-wide re-point
|
|
28
|
+
(`abx set-token-uri` / `abx set-renderer`) has no range form of `URI` to express "every id may have
|
|
29
|
+
changed" (looping the whole id space would be a block-gas trap), and a per-id override write skips it
|
|
30
|
+
when an on-chain renderer is configured (the event would carry the entire rendered document). So a
|
|
31
|
+
marketplace/indexer that honors *only* the native per-id event can miss it. Fix: `abx ping-uri <addr>
|
|
32
|
+
--token-ids <csv|range>` re-emits the per-id refresh events for the ids you name — **owner-only** (it
|
|
33
|
+
used to be permissionless; with an on-chain renderer, letting anyone make an indexer render a whole
|
|
34
|
+
document per id was an asymmetric spam primitive), so sign it as the collection owner. It always emits
|
|
35
|
+
ERC-4906 `MetadataUpdate(id)`; the native `URI(uri(id), id)` follows only when there is **no** on-chain
|
|
36
|
+
renderer configured — with one, that event would carry the entire rendered document.
|
|
37
|
+
(Heads-up: `abx refresh`'s own advisory text still says an edition emits no ERC-4906. That line is
|
|
38
|
+
stale against the contracts; the `ping-uri` advice it gives is still the right next step.)
|
|
39
|
+
|
|
40
|
+
### "It's registered but serves nothing" / the add seemed to hang
|
|
41
|
+
**Ask the service where it got to before doing anything else: `abx status <addr> --remote <name>`.** The five states and what each one means for you are in [hosting.md → Indexing status](hosting.md#indexing-status--backfilling-is-not-a-failure). In short: `queued`/`backfilling` = **wait** (a cold replay on a rate-limited RPC takes minutes; the registration is already durable, so re-adding just makes you wait again); `failed` = read `error.class` (`rpc_rate_limited`/`rpc_unavailable` are the *service's* RPC, not your key — it retries on backoff); `stale` = it's serving, just behind. Never diagnose this by redeploying, rotating the key, or re-pointing the on-chain URI.
|
|
42
|
+
|
|
43
|
+
**On your own node:** the same command without `--remote`. A project stuck `queued` on a node that isn't running `abx serve` has nothing to drain it → start the resolver (or run `abx index <addr>` once). `stale` on your own node means the chain watcher has fallen far behind head — check the node's RPC.
|
|
20
44
|
|
|
21
45
|
### `abx tokenuri` / Etherscan reverts on a fully-on-chain code project
|
|
22
|
-
A large on-chain `tokenURI` document can exceed the **unauthenticated eth_call gas cap** some RPCs/explorers impose on a public read — the call reverts in that UI but the data is fine on a normal RPC. This is **expected for a big on-chain doc, NOT an indexing problem** — do not `abx index --full` or redeploy. Read it via a
|
|
46
|
+
A large on-chain `tokenURI` document can exceed the **unauthenticated eth_call gas cap** some RPCs/explorers impose on a public read — the call reverts in that UI but the data is fine on a normal RPC. This is **expected for a big on-chain doc, NOT an indexing problem** — do not `abx index --full` (there is no index in the on-chain path) or redeploy. Read it via `abx tokenuri`, a wallet-connected read, or a high-gas RPC.
|
|
23
47
|
|
|
24
48
|
### The on-chain tokenURI points at `localhost`
|
|
25
49
|
The base URL baked on-chain is a localhost/placeholder (a dev-escape deploy, or a base set without a public host) → it resolves for no one. Re-point to a public resolver: `abx set-token-uri <addr> --uri https://<your-resolver>` (+ `set-contract-uri`), then `abx refresh`. A normal `deploy-code`/`deploy` **refuses** a localhost base — this only happens via the `ABX_DEV_ALLOW_LOCALHOST_URI` dev escape.
|
|
26
50
|
|
|
51
|
+
### `deploy-code` deployed the contract but the SETUP transaction failed
|
|
52
|
+
A code deploy is **two** transactions: create the clone, then one atomic setup `multicall` (script chunks + schemas + dependencies + the on-chain-URI legs + any reserve mints). When the second fails you own a contract that exists but has no program — `abx verify` reports no code, the live view 404s — and the CREATE2 salt for that address is **spent**, so the dry run's pinned-salt re-run command lands somewhere else now.
|
|
53
|
+
|
|
54
|
+
**The contract is recoverable. Do NOT redeploy, and do NOT hand-assemble a multicall with `cast`.** Finish it:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
abx deploy-code --resume <address> <the SAME content flags the original deploy used> # add --dry-run first
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
It reads what is already on-chain and sends only what is missing, in one transaction — so it is safe to run twice, and if nothing is missing it sends nothing and tells you so. Chunks are compared by **content** (a partial hand repair is respected), a schema that already exists is left alone, and reserve mints are a **shortfall** against current supply, never a re-send. You must pass the script/`--code-dir` again: those bytes are not recoverable from a failed transaction. `--salt`, `--721c`, `--bootstrap-factory` and `--mint-all` are refused — they describe how a contract is *created*, and 721C enrollment in particular can never be added after deploy.
|
|
61
|
+
|
|
62
|
+
If the DEPLOY (first) transaction is what failed, there is nothing to resume — no contract exists. Run a normal deploy.
|
|
63
|
+
|
|
27
64
|
### `abx index`/`abx verify` says "isn't registered"
|
|
28
65
|
Register the project on this node once: `abx add <addr>` (discovers the deploy block, indexes it). Then `index`/`verify` work. `state`/`tokenuri` never need this.
|
package/dist/inspect.d.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `abx inspect <script.js>` — static analysis of a generative script, BEFORE picking a lane.
|
|
3
|
-
*
|
|
4
|
-
* The membrane fix behind this: an agent (or creator) should DERIVE the deployment lane from what
|
|
5
|
-
* the script actually needs — are there traits? are they reproducible on-chain? how big is the
|
|
6
|
-
* assembled document (does a single `tokenURI` eth_call even fit)? — instead of guessing "fully
|
|
7
|
-
* on-chain!" and walking it back. This module is the pure analysis; `cmdInspect` formats it.
|
|
8
|
-
*
|
|
9
|
-
* Static only — it never executes the script (that's the resolver/runner's job, and executing
|
|
10
|
-
* untrusted art in the CLI would be a footgun). Everything here is regex/heuristic over the source.
|
|
11
|
-
*/
|
|
12
|
-
export type TraitFeasibility = 'none' | 'exact-likely' | 'careful' | 'infeasible' | 'unknown';
|
|
13
|
-
export interface ScriptAnalysis {
|
|
14
|
-
bytes: number;
|
|
15
|
-
estChunks: number;
|
|
16
|
-
traits: {
|
|
17
|
-
present: boolean;
|
|
18
|
-
keys: string[];
|
|
19
|
-
};
|
|
20
|
-
paramHints: string[];
|
|
21
|
-
prng: {
|
|
22
|
-
seeded: boolean;
|
|
23
|
-
usesBareRandom: boolean;
|
|
24
|
-
usesMathRandom: boolean;
|
|
25
|
-
usesNoise: boolean;
|
|
26
|
-
};
|
|
27
|
-
depHints: string[];
|
|
28
|
-
looksP5: boolean;
|
|
29
|
-
feasibility: {
|
|
30
|
-
verdict: TraitFeasibility;
|
|
31
|
-
reason: string;
|
|
32
|
-
};
|
|
33
|
-
doc: {
|
|
34
|
-
estBytes: number;
|
|
35
|
-
deps: string[];
|
|
36
|
-
fitsSingleCall: boolean;
|
|
37
|
-
unknownDepSizes: boolean;
|
|
38
|
-
};
|
|
39
|
-
runtime: {
|
|
40
|
-
readsTokenData: boolean;
|
|
41
|
-
reportsTraits: boolean;
|
|
42
|
-
wrongGlobal: string | null;
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
export declare function analyzeScript(source: string, declaredDeps?: string[]): ScriptAnalysis;
|
|
46
|
-
/** A one-line lane recommendation derived from the analysis (the decision-tree output). */
|
|
47
|
-
export declare function recommendLane(a: ScriptAnalysis): string;
|
|
48
|
-
//# sourceMappingURL=inspect.d.ts.map
|
package/dist/inspect.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../src/inspect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAgBH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,cAAc,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;AAE9F,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAC,CAAC;IAC3C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAC,CAAC;IAC9F,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE;QAAC,OAAO,EAAE,gBAAgB,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC;IACzD,GAAG,EAAE;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,cAAc,EAAE,OAAO,CAAC;QAAC,eAAe,EAAE,OAAO,CAAA;KAAC,CAAC;IAM3F,OAAO,EAAE;QAAC,cAAc,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAC,CAAC;CACxF;AAsDD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,GAAE,MAAM,EAAO,GAAG,cAAc,CAqEzF;AAED,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CA0BvD"}
|