@artblocks/abx-cli 0.1.0-alpha.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/CHANGELOG.md +429 -0
  2. package/assets/renderer-scaffold/README.md +44 -13
  3. package/assets/renderer-scaffold/foundry.toml +5 -0
  4. package/assets/renderer-scaffold/remappings.txt +1 -1
  5. package/assets/renderer-scaffold/script/DeployHooks.s.sol +24 -0
  6. package/assets/renderer-scaffold/script/Preview.s.sol +99 -0
  7. package/assets/renderer-scaffold/src/MyHooks.sol +20 -0
  8. package/assets/renderer-scaffold/src/MyRenderer.sol +4 -4
  9. package/assets/renderer-scaffold/src/MyTraits.sol +2 -2
  10. package/assets/renderer-scaffold/test/MyRenderer.t.sol +60 -3
  11. package/dist/bin.d.ts +26 -0
  12. package/dist/bin.d.ts.map +1 -0
  13. package/dist/bin.js +63 -0
  14. package/dist/bin.js.map +1 -0
  15. package/dist/capabilities.d.ts +132 -0
  16. package/dist/capabilities.d.ts.map +1 -0
  17. package/dist/capabilities.js +175 -0
  18. package/dist/capabilities.js.map +1 -0
  19. package/dist/commands/auth.d.ts +54 -0
  20. package/dist/commands/auth.d.ts.map +1 -0
  21. package/dist/commands/auth.js +483 -0
  22. package/dist/commands/auth.js.map +1 -0
  23. package/dist/commands/contract.d.ts +19 -0
  24. package/dist/commands/contract.d.ts.map +1 -0
  25. package/dist/commands/contract.js +171 -0
  26. package/dist/commands/contract.js.map +1 -0
  27. package/dist/commands/deploy.d.ts +296 -0
  28. package/dist/commands/deploy.d.ts.map +1 -0
  29. package/dist/commands/deploy.js +5756 -0
  30. package/dist/commands/deploy.js.map +1 -0
  31. package/dist/commands/feedback.d.ts +7 -0
  32. package/dist/commands/feedback.d.ts.map +1 -0
  33. package/dist/commands/feedback.js +154 -0
  34. package/dist/commands/feedback.js.map +1 -0
  35. package/dist/commands/maintenance.d.ts +4 -0
  36. package/dist/commands/maintenance.d.ts.map +1 -0
  37. package/dist/commands/maintenance.js +114 -0
  38. package/dist/commands/maintenance.js.map +1 -0
  39. package/dist/commands/project.d.ts +458 -0
  40. package/dist/commands/project.d.ts.map +1 -0
  41. package/dist/commands/project.js +1837 -0
  42. package/dist/commands/project.js.map +1 -0
  43. package/dist/commands/reads.d.ts +85 -0
  44. package/dist/commands/reads.d.ts.map +1 -0
  45. package/dist/commands/reads.js +881 -0
  46. package/dist/commands/reads.js.map +1 -0
  47. package/dist/commands/scaffold.d.ts +89 -0
  48. package/dist/commands/scaffold.d.ts.map +1 -0
  49. package/dist/commands/scaffold.js +741 -0
  50. package/dist/commands/scaffold.js.map +1 -0
  51. package/dist/commands/service.d.ts +67 -0
  52. package/dist/commands/service.d.ts.map +1 -0
  53. package/dist/commands/service.js +761 -0
  54. package/dist/commands/service.js.map +1 -0
  55. package/dist/commands/storage.d.ts +51 -0
  56. package/dist/commands/storage.d.ts.map +1 -0
  57. package/dist/commands/storage.js +399 -0
  58. package/dist/commands/storage.js.map +1 -0
  59. package/dist/commands/submit-app.d.ts +102 -0
  60. package/dist/commands/submit-app.d.ts.map +1 -0
  61. package/dist/commands/submit-app.js +638 -0
  62. package/dist/commands/submit-app.js.map +1 -0
  63. package/dist/config.d.ts +94 -2
  64. package/dist/config.d.ts.map +1 -1
  65. package/dist/config.js +332 -12
  66. package/dist/config.js.map +1 -1
  67. package/dist/conformance.d.ts +31 -0
  68. package/dist/conformance.d.ts.map +1 -0
  69. package/dist/conformance.js +400 -0
  70. package/dist/conformance.js.map +1 -0
  71. package/dist/contract-read-error.d.ts +5 -0
  72. package/dist/contract-read-error.d.ts.map +1 -0
  73. package/dist/contract-read-error.js +37 -0
  74. package/dist/contract-read-error.js.map +1 -0
  75. package/dist/creator-agent.d.ts +49 -0
  76. package/dist/creator-agent.d.ts.map +1 -0
  77. package/dist/creator-agent.js +363 -0
  78. package/dist/creator-agent.js.map +1 -0
  79. package/dist/creator-keyring.d.ts +26 -0
  80. package/dist/creator-keyring.d.ts.map +1 -0
  81. package/dist/creator-keyring.js +57 -0
  82. package/dist/creator-keyring.js.map +1 -0
  83. package/dist/creator-signer.d.ts +52 -0
  84. package/dist/creator-signer.d.ts.map +1 -0
  85. package/dist/creator-signer.js +267 -0
  86. package/dist/creator-signer.js.map +1 -0
  87. package/dist/deploy-plan.d.ts +218 -0
  88. package/dist/deploy-plan.d.ts.map +1 -0
  89. package/dist/deploy-plan.js +20 -0
  90. package/dist/deploy-plan.js.map +1 -0
  91. package/dist/deps.d.ts +6 -39
  92. package/dist/deps.d.ts.map +1 -1
  93. package/dist/deps.js +4 -68
  94. package/dist/deps.js.map +1 -1
  95. package/dist/errors.d.ts +22 -0
  96. package/dist/errors.d.ts.map +1 -0
  97. package/dist/errors.js +32 -0
  98. package/dist/errors.js.map +1 -0
  99. package/dist/flag-allowlists.d.ts +53 -0
  100. package/dist/flag-allowlists.d.ts.map +1 -0
  101. package/dist/flag-allowlists.js +188 -0
  102. package/dist/flag-allowlists.js.map +1 -0
  103. package/dist/flags.d.ts +53 -0
  104. package/dist/flags.d.ts.map +1 -1
  105. package/dist/flags.js +151 -1
  106. package/dist/flags.js.map +1 -1
  107. package/dist/jsonout.d.ts +37 -0
  108. package/dist/jsonout.d.ts.map +1 -0
  109. package/dist/jsonout.js +79 -0
  110. package/dist/jsonout.js.map +1 -0
  111. package/dist/kind.d.ts +57 -0
  112. package/dist/kind.d.ts.map +1 -0
  113. package/dist/kind.js +122 -0
  114. package/dist/kind.js.map +1 -0
  115. package/dist/main.js +859 -5374
  116. package/dist/main.js.map +1 -1
  117. package/dist/mintpage.d.ts +17 -2
  118. package/dist/mintpage.d.ts.map +1 -1
  119. package/dist/mintpage.js +241 -54
  120. package/dist/mintpage.js.map +1 -1
  121. package/dist/output.d.ts +184 -0
  122. package/dist/output.d.ts.map +1 -0
  123. package/dist/output.js +774 -0
  124. package/dist/output.js.map +1 -0
  125. package/dist/ownerops.d.ts +463 -59
  126. package/dist/ownerops.d.ts.map +1 -1
  127. package/dist/ownerops.js +2329 -366
  128. package/dist/ownerops.js.map +1 -1
  129. package/dist/preview.d.ts +23 -5
  130. package/dist/preview.d.ts.map +1 -1
  131. package/dist/preview.js +95 -43
  132. package/dist/preview.js.map +1 -1
  133. package/dist/provision.d.ts +3 -13
  134. package/dist/provision.d.ts.map +1 -1
  135. package/dist/provision.js +19 -21
  136. package/dist/provision.js.map +1 -1
  137. package/dist/remote.d.ts +116 -9
  138. package/dist/remote.d.ts.map +1 -1
  139. package/dist/remote.js +371 -14
  140. package/dist/remote.js.map +1 -1
  141. package/dist/riskgate.d.ts +101 -0
  142. package/dist/riskgate.d.ts.map +1 -0
  143. package/dist/riskgate.js +343 -0
  144. package/dist/riskgate.js.map +1 -0
  145. package/dist/scaffold.d.ts +12 -0
  146. package/dist/scaffold.d.ts.map +1 -0
  147. package/dist/scaffold.js +56 -0
  148. package/dist/scaffold.js.map +1 -0
  149. package/dist/schema.d.ts +36 -1
  150. package/dist/schema.d.ts.map +1 -1
  151. package/dist/schema.js +121 -26
  152. package/dist/schema.js.map +1 -1
  153. package/dist/script-chunks.d.ts +8 -0
  154. package/dist/script-chunks.d.ts.map +1 -0
  155. package/dist/script-chunks.js +35 -0
  156. package/dist/script-chunks.js.map +1 -0
  157. package/dist/served.d.ts +30 -0
  158. package/dist/served.d.ts.map +1 -0
  159. package/dist/served.js +112 -0
  160. package/dist/served.js.map +1 -0
  161. package/dist/signer.d.ts +20 -1
  162. package/dist/signer.d.ts.map +1 -1
  163. package/dist/signer.js +110 -18
  164. package/dist/signer.js.map +1 -1
  165. package/dist/update-check.d.ts +60 -9
  166. package/dist/update-check.d.ts.map +1 -1
  167. package/dist/update-check.js +105 -18
  168. package/dist/update-check.js.map +1 -1
  169. package/package.json +17 -12
  170. package/skill/SKILL.md +213 -354
  171. package/skill/agents/openai.yaml +4 -0
  172. package/skill/reference/capabilities.md +195 -0
  173. package/skill/reference/code.md +236 -0
  174. package/skill/reference/creator-token.md +94 -0
  175. package/skill/reference/deploy.md +191 -0
  176. package/skill/reference/diagnose.md +178 -0
  177. package/skill/reference/hosting.md +191 -102
  178. package/skill/reference/operate.md +237 -0
  179. package/skill/reference/services.md +173 -0
  180. package/skill/reference/setup.md +221 -42
  181. package/assets/renderer-scaffold/src/interfaces/IAbxFieldRenderer.sol +0 -32
  182. package/assets/renderer-scaffold/src/interfaces/IAbxParams.sol +0 -26
  183. package/dist/inspect.d.ts +0 -48
  184. package/dist/inspect.d.ts.map +0 -1
  185. package/dist/inspect.js +0 -184
  186. package/dist/inspect.js.map +0 -1
  187. package/dist/migrate.d.ts +0 -65
  188. package/dist/migrate.d.ts.map +0 -1
  189. package/dist/migrate.js +0 -180
  190. package/dist/migrate.js.map +0 -1
  191. package/dist/onchain-uri.d.ts +0 -97
  192. package/dist/onchain-uri.d.ts.map +0 -1
  193. package/dist/onchain-uri.js +0 -243
  194. package/dist/onchain-uri.js.map +0 -1
  195. package/dist/upload.d.ts +0 -28
  196. package/dist/upload.d.ts.map +0 -1
  197. package/dist/upload.js +0 -41
  198. package/dist/upload.js.map +0 -1
  199. package/skill/reference/code-projects.md +0 -246
  200. package/skill/reference/operating.md +0 -118
  201. package/skill/reference/troubleshooting.md +0 -28
@@ -1,42 +1,221 @@
1
- # Environment detail — RPC & troubleshooting
2
-
3
- [← back to SKILL.md](../SKILL.md)
4
-
5
- ## Choosing an RPC (drives re-index speed)
6
-
7
- 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.**
8
-
9
- When `abx doctor` flags a tight range, or a reconstruction would be large/slow — **a decision point: research and present, don't power through:**
10
- 1. **Do your own current research** (the limits rot) — find a free, range-generous, archive-capable endpoint for this chain *right now*.
11
- 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
- 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
-
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.
15
-
16
- **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)).
17
-
18
- **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.
19
-
20
- ## Troubleshooting
21
-
22
- - **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 `SEPOLIA_WALLET_PK` (or `SEPOLIA_FUNDED_PK` / `ABX_DEPLOYER_PK` — any one) 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).
23
- - **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.
24
- - **storage 'cloud' missing accessKeyId/…** → set `ABX_S3_*` in `.env`. **'ipfs' unreachable** → Kubo not running or `PINATA_JWT` missing.
25
- - **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.
26
- - **`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":
27
- - **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.)
28
- - **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.
29
- - **`/a/…` returns `{"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 `code` field written at deploy — a scan-floor bug, **not** a resolver version/compat gap (do NOT redeploy as a static NFT). 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.)
30
- - **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.
31
- - **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>`.
32
- - **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.
33
- - **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.
34
- - **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.
35
- - **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."
36
- - **serving code projects is RPC-heavier than static art** → 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.
37
- - **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_RESOLVER_ADMIN_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_RESOLVER_ADMIN_TOKEN`.
38
- - **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>`).
39
- - **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.
40
- - **`/.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.
41
- - **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.
42
- - **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.
1
+ # Setup and safety
2
+
3
+ Use this reference when installing ABX, selecting a binary or signer, configuring a new environment,
4
+ or preparing a real transaction.
5
+
6
+ ## Contents
7
+
8
+ - [Resolve the tool before the project](#resolve-the-tool-before-the-project)
9
+ - [Upgrade when a release is available](#upgrade-when-a-release-is-available)
10
+ - [Never expose secrets](#never-expose-secrets)
11
+ - [Select the chain explicitly through the environment](#select-the-chain-explicitly-through-the-environment)
12
+ - [Choose one signing lane](#choose-one-signing-lane)
13
+ - [Prepare without mutating](#prepare-without-mutating)
14
+ - [New environment checklist](#new-environment-checklist)
15
+
16
+ ## Resolve the tool before the project
17
+
18
+ Inside the ABX source repository run `pnpm abx …`; elsewhere run the installed `abx`. A bare global
19
+ binary inside the repository may be older than the source. Start with:
20
+
21
+ ```bash
22
+ abx version
23
+ abx doctor
24
+ abx capabilities --json
25
+ abx skill install
26
+ ```
27
+
28
+ `abx doctor` reports binary provenance, CLI/skill drift, active chain, RPC health and history reach,
29
+ wallet readiness, canonical factories, storage, and configured remotes without exposing credentials.
30
+ Treat a red check as an actionable setup state, not as permission to improvise a workaround.
31
+
32
+ The shipped skill is named `abx`. `abx skill install` installs it to `.claude/skills/abx` and
33
+ `.agents/skills/abx` by default; `--agent`, `--global`, and `--target` narrow the destination. During
34
+ the rename transition the installer moves a recognized `abx-self-host` folder to
35
+ `.abx-skill-backups/` before installing the new name. It preserves custom bytes rather than deleting
36
+ them. For a custom skills parent, run `abx skill install --target <that-parent>`; do not move or merge
37
+ the old folder manually. Restart the agent after installation.
38
+
39
+ ABX requires Node 22.13 or newer. Prefer a project-local npm dependency for reproducible automation;
40
+ use a global install for interactive machine-wide use. Avoid repeated `npx` execution when version
41
+ provenance matters because caches can outlive an upgrade.
42
+
43
+ ## Upgrade when a release is available
44
+
45
+ Treat an `update available` notice or an outdated `npm` row in `abx doctor` as a recommendation to
46
+ upgrade before planning a deployment or preparing a write. Do not silently change a global install
47
+ or a project's dependencies; tell the creator which version is installed, which version is
48
+ available, and give the command for that install:
49
+
50
+ ```bash
51
+ # Global install
52
+ npm install -g @artblocks/abx-cli@latest
53
+
54
+ # Project-local install
55
+ npm install --save-dev @artblocks/abx-cli@latest
56
+ ```
57
+
58
+ The latest ABX release retains support for canonical projects made with earlier releases unless a
59
+ specific contract generation is explicitly quarantined for a security or correctness reason.
60
+ Updating the CLI does not redeploy or mutate a project. New deployments may use newer factories or
61
+ defaults, so rerun help, capabilities, and the JSON dry run after upgrading.
62
+
63
+ Then refresh the copied skill in the same scope reported by the CLI, restart the agent, and check the
64
+ environment again:
65
+
66
+ ```bash
67
+ abx skill install # project copy
68
+ abx skill install --global # global copy, when reported
69
+ abx doctor
70
+ ```
71
+
72
+ If the creator defers the upgrade, say so once and continue only with the installed release's live
73
+ help and capability output. Never assume a newer flag or behavior exists in an older binary.
74
+
75
+ ## Never expose secrets
76
+
77
+ Do not open, search, quote, or print `.env`. Do not echo variables to test whether they exist. Do not
78
+ paste a private key into a command line, generated file, wallet page, bug report, or transcript.
79
+
80
+ Sensitive values include:
81
+
82
+ - `ABX_DEPLOYER_PK` and any wallet key;
83
+ - credential-bearing `ABX_RPC_URLS*` values;
84
+ - `PINATA_JWT`, S3/R2 keys, `ABX_SERVICES_API_KEY`, other remote-provider tokens, effects/admin
85
+ tokens;
86
+ - Arweave JWK contents and browser signing-session URLs.
87
+
88
+ Use `abx doctor`, `abx remote <name>`, `abx storage show --check`, and redacted CLI errors. If a tool
89
+ ever emits an unredacted credential-bearing endpoint, stop, rotate the credential, and report the
90
+ output bug without repeating the secret.
91
+
92
+ ## Select the chain explicitly through the environment
93
+
94
+ Read the full `chains` array from `abx capabilities --json`. Do not rely on a remembered network
95
+ list. ABX is adding networks, and status may change between releases. There is no `--chain` flag
96
+ because ignoring a wrong-chain request could spend on the wrong network.
97
+
98
+ Choose a production chain by intended users, then measured cost. Use the `pairedChain` reported by
99
+ capabilities for the test run. Large onchain payloads cost more on every network, so compare the dry
100
+ run with Arweave, IPFS, or cloud custody.
101
+
102
+ Before any transaction on an `experimental` or `beta` network, name the network and support level,
103
+ explain what the transaction or transaction group will do, and state the relevant contract,
104
+ configuration, and real-funds risks. ABX has not had an independent third-party audit; bugs,
105
+ misconfiguration, and nondeterministic agent behavior may cause permanent loss. Link to the open
106
+ source, use-at-your-own-risk implementation at
107
+ https://github.com/ArtBlocks/abx. Qualify the same flow on the paired testnet before production.
108
+
109
+ Use per-chain RPC variables when operating more than one chain. `abx doctor` checks chain identity,
110
+ wide-range `eth_getLogs`, archival reach, and nonce coherence. Put a healthy archive endpoint first:
111
+ fallback transports rotate on errors, not on a successful but pruned empty log response.
112
+
113
+ The local projection and managed Arweave identity remain in `.abx-self-host/` unless
114
+ `ABX_DATA_DIR` overrides it. That runtime directory is separate from the renamed `abx` skill and is
115
+ not being renamed. Back up the managed Arweave key with `abx storage backup-key`; never print it.
116
+
117
+ Every WRITE command (`deploy*`, `add`, `index`, `mint`, `set-*`, …) resolves `.abx-self-host`
118
+ strictly relative to the current directory — it never searches upward, so it never creates a
119
+ project's state somewhere unexpected. A handful of READ commands (`status`, `state`, `verify`,
120
+ `doctor`, `capabilities`, `tokens`, `tokenuri`, `contracturi`, `inspect`, `minter show`) DO search
121
+ upward, git-style, for an already-existing `.abx-self-host` if the current directory doesn't have
122
+ one of its own — bounded at the home directory, a `.git` root, or the filesystem root. Practical
123
+ consequence: `cd`-ing into a project's `contracts/` subdirectory before `abx status` still finds
124
+ that project; the same `cd` before `abx add`/`abx deploy*` creates a NEW, empty node right there
125
+ instead. If a status/verify/etc. answer looks emptier than expected, or a write seems to have
126
+ landed in the wrong place, run bare `abx status` — its `data: <path>` line names the exact
127
+ directory that answered, including a note when it was found by searching upward — or set
128
+ `ABX_DATA_DIR` explicitly rather than guessing. See [Local data
129
+ directory](https://docs.abx.io/docs/using-abx/self-hosting#local-data-directory) for the full rule.
130
+
131
+ ## Choose one signing lane
132
+
133
+ Every write uses one of four lanes:
134
+
135
+ | Lane | Select | Use when |
136
+ |---|---|---|
137
+ | Sponsored | `--sponsor` | The live remote advertises sponsorship for this chain and command |
138
+ | Hot | `--send` or default | An environment key may sign unattended |
139
+ | Wallet | `--sign --for 0x…` | A human approves in their browser wallet |
140
+ | Cold | `--unsigned --for 0x…` | A multisig/offline signer needs prepared transactions |
141
+
142
+ Honor an explicit creator choice. Otherwise run `abx capabilities --json`, the command help, and
143
+ `abx remote abx`. Prefer `--sponsor` when the provider advertises the active chain, the command and
144
+ transaction group are eligible, and sponsorship is enabled—especially on a testnet, where it removes
145
+ wallet and faucet setup. Sponsorship is provider policy, not protocol support: if it is unavailable,
146
+ paused, or ineligible, fall back to the appropriate bring-your-own lane rather than treating the ABX
147
+ operation as unsupported.
148
+
149
+ Select the lane before the JSON dry run. The signer can affect ownership, salts, and predicted
150
+ addresses. If the lane changes, rerun the dry run and summarize the revised plan before execution.
151
+
152
+ Run `abx doctor --for <address>` before a wallet or cold operation. On the wallet lane, give the
153
+ human the locally generated signing page; never request or handle their key. On the cold lane, verify
154
+ chain, sender, target, calldata, value, and ordering before handing transactions over.
155
+
156
+ `--onchain-image` staging cannot use the cold lane because each chunk transaction depends on the
157
+ receipt of the preceding transaction. Use hot, wallet, or sponsored signing. Do not split staging
158
+ into a homemade offline sequence.
159
+
160
+ `ABX_DEPLOYER_PK` may be written with or without a `0x` prefix — the CLI and SDK normalize it. Foundry's
161
+ `vm.envUint` does not: a custom forge script that reads the same `.env` needs the `0x` form.
162
+
163
+ For any one EOA, run one write command at a time. ABX obtains pending and latest nonces once, takes the
164
+ safe maximum, increments locally, waits for newly deployed code when a following transaction targets
165
+ it, and throws typed reversion errors. Starting concurrent processes bypasses that serialization.
166
+
167
+ ## Prepare without mutating
168
+
169
+ Use command help and dry runs, not guessed syntax:
170
+
171
+ ```bash
172
+ abx help deploy-code
173
+ abx deploy-code … --dry-run --json
174
+ ```
175
+
176
+ A dry run may read contracts, probe endpoints, analyze files, or verify an address, but it does not
177
+ send or store. Address prediction is meaningful only when the salt and deployer are pinned. Use
178
+ `abx predict` when another contract or resolver must know the collection address before deployment.
179
+
180
+ Before any real send, confirm:
181
+
182
+ - active chain and deploying/owning address;
183
+ - contract family and edition arithmetic;
184
+ - name, symbol, royalties and royalty ceiling;
185
+ - deploy-time options such as burnability and creator-token enrollment;
186
+ - custody, public resolution, image/animation/trait surfaces;
187
+ - mint amount, value, transaction count, and expected gas;
188
+ - every irreversible lock or authority transfer.
189
+
190
+ Do not make a real send merely because a dry run exited successfully. The dry run is the plan; the
191
+ human confirmation authorizes execution.
192
+
193
+ ## New environment checklist
194
+
195
+ 1. Install Node 22.13+ and the desired CLI version.
196
+ 2. Install the co-versioned skill with `abx skill install`, then restart the agent.
197
+ 3. Select the testnet with `ABX_CHAIN` when the default is not appropriate.
198
+ 4. Add RPC and storage configuration outside the transcript. If the live first-party remote
199
+ advertises sponsorship for the chosen chain and command, recommend it before asking the creator
200
+ to configure a signer. Otherwise configure one of the bring-your-own lanes. For first-party hosted
201
+ services, run `abx auth login --no-open` in a short-yield or resumable session: immediately hand
202
+ its verified browser URL and matching code to the human, then resume that same polling process.
203
+ Let the CLI store `ABX_SERVICES_API_KEY` in ignored `.env` without printing it. Never ask for the
204
+ email OTP or key in chat, start duplicate login sessions, or add standing login instructions to
205
+ `AGENTS.md`/`CLAUDE.md`. The key is long-lived: reuse it across tasks and agent sessions. Run
206
+ `abx auth keys` before creating a separate credential if the account is at its five-key limit;
207
+ revoke an unused non-current key with `abx auth revoke-key <key-id>`. Run `abx auth logout` only
208
+ for intentional teardown, compromise, rotation, or cleanup of the current key.
209
+ 5. Run `abx doctor` and resolve every red check relevant to the chosen lane.
210
+ 6. Run `abx storage show --check` when bytes will leave the local disk.
211
+ 7. Run `abx remote <name>` before relying on a configured managed service.
212
+ 8. Run command help, then a JSON dry run.
213
+ 9. Confirm the plan and only then execute.
214
+
215
+ For an eligible first-party account, `--sponsor` replaces local key and faucet setup with an
216
+ account-bound creator wallet. The first use opens a Privy agent grant; later commands reuse that
217
+ grant for up to 30 days, unless the creator revokes it first. Do not ask for another browser approval when the
218
+ CLI resumes an existing grant. Use sponsorship only when the live descriptor and account
219
+ capabilities advertise the active chain. Test the exact flow on the reported paired testnet before
220
+ production. Read [services.md](services.md) before using it. Sponsorship is zero-value and never a
221
+ reason to skip the dry run or transaction summary.
@@ -1,32 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.20;
3
-
4
- /// @title IAbxFieldRenderer — the contract your in-chain art implements
5
- /// @notice A field renderer returns ONE metadata field's value, COMPUTED from chain state
6
- /// (the token's seed + PostParams, its owner, anything readable on-chain). The ABX
7
- /// metadata renderer staticcalls it to build `tokenURI` — e.g. an `image` renderer's
8
- /// SVG becomes `data:image/svg+xml;base64,…`. Deploy your renderer, then wire it with
9
- /// `abx deploy-code --image-renderer <address> [--attributes-renderer <address>] --onchain-uri`.
10
- ///
11
- /// @dev THE FIVE INVARIANTS (a reverting or malformed renderer bricks the WHOLE tokenURI —
12
- /// the metadata renderer staticcalls you with NO try/catch):
13
- /// 1. NEVER revert for ANY (token, tokenId, field) — including the collection surface
14
- /// `tokenId == type(uint256).max` (used by `contractURI`). Return a neutral value.
15
- /// 2. Return the correct `contentType` (`image/svg+xml` for an image; `application/json`
16
- /// for an `attributes` array).
17
- /// 3. Guard `field`: revert only a truly unsupported field (a deploy-time miswiring), but
18
- /// still never revert on the fields you DO render.
19
- /// 4. `view` + deterministic: same chain state → same bytes.
20
- /// 5. Bounded output: keep it small (a few hundred bytes of SVG) so `tokenURI` stays a
21
- /// cheap single eth_call. `forge test` the never-revert cases before you wire it.
22
- interface IAbxFieldRenderer {
23
- /// @param token the ABX collection contract (read its params via {IAbxParams}).
24
- /// @param tokenId the token, or `type(uint256).max` for the collection surface.
25
- /// @param field which field to compute (e.g. `"image"`, `"attributes"`).
26
- /// @return contentType MIME of `data` (e.g. `image/svg+xml`, `application/json`).
27
- /// @return data the field's finished bytes.
28
- function render(address token, uint256 tokenId, bytes32 field)
29
- external
30
- view
31
- returns (string memory contentType, bytes memory data);
32
- }
@@ -1,26 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.20;
3
-
4
- /// @title IAbxParams — read the token's on-chain params (seed + collector PostParams)
5
- /// @notice Your renderer reads live state through this. Each value is a `bytes32`; `isSet` tells
6
- /// you whether it exists (fall back to a default if not — see invariant #1). `valueIsHash`
7
- /// is true when the on-chain value is a keccak commitment to off-chain bytes (a literal
8
- /// scalar param like a `seed` or a `HexColor` palette is NOT a hash — check `!valueIsHash`).
9
- ///
10
- /// The tokenData merge rule: a token-scope value overrides the contract-scope one. The
11
- /// common pattern is `tokenParam` first, then `contractParam` as a fallback (see the
12
- /// `_param` helper in MyRenderer.sol).
13
- interface IAbxParams {
14
- /// Returns the token-scope param: its bytes32 `value`, `valueIsHash` (true if it commits to
15
- /// off-chain bytes — a literal scalar like a seed/HexColor is false), and `isSet` (does it exist).
16
- function tokenParam(uint256 tokenId, bytes32 key)
17
- external
18
- view
19
- returns (bytes32 value, bool valueIsHash, bool isSet);
20
-
21
- /// @notice The contract-scope (collection-wide) value — the fallback when a token has none.
22
- function contractParam(bytes32 key)
23
- external
24
- view
25
- returns (bytes32 value, bool valueIsHash, bool isSet);
26
- }
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
@@ -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"}
package/dist/inspect.js DELETED
@@ -1,184 +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
- /** Approx bytes a known on-chain dependency adds to the ASSEMBLED document (gzip'd + base64'd, as it
13
- * rides in the inline data-URI). Measured from real drops; used only for the RPC-size estimate. */
14
- const KNOWN_DEP_DOC_BYTES = {
15
- 'p5@1.0.0': 200_000, // p5 1.0.0 (~623KB raw) rides ~200KB gzip+base64 in the document
16
- };
17
- /** The on-chain runtime the generator always inlines (abx.js + the gunzip bootstrap), base64'd. */
18
- const RUNTIME_DOC_BYTES = 13_000;
19
- /** A single `eth_call` returning `tokenURI` comfortably carries this much document on a default node
20
- * (geth's 50M gas cap; memory expansion is ~quadratic). Past it, marketplaces that call `tokenURI`
21
- * once may time out — prefer the generator's piecewise getters, directory mode, or a CDN dep. */
22
- const SINGLE_CALL_DOC_CEILING = 1_500_000;
23
- /** PostParam keys the script READS from tokenData — the palette-style customization inputs. A deploy
24
- * that omits these from `--schema` silently drops them (the render sees `undefined` → its default),
25
- * which is exactly how a real session "forgot" the palette it had itself identified. Heuristic (the
26
- * script isn't executed): find identifiers aliased to `abx.tokenData`, then their property reads;
27
- * plus direct `abx.tokenData.key`, destructuring, and `tokenData['key']`. `seed` is intrinsic, not a
28
- * PostParam, so it's excluded. Advisory — over- or under-detection is a hint, never a hard gate. */
29
- function paramKeys(source) {
30
- const keys = new Set();
31
- const add = (k) => { if (k && k !== 'seed')
32
- keys.add(k); };
33
- // Identifiers aliased to the token-data object — either `abx.tokenData` (via abx.js) or the raw
34
- // injected global `window.abxTokenData` (e.g. `var td = (window.abx && abx.tokenData) || {}`).
35
- const aliases = new Set();
36
- // The `(?!…\.[A-Za-z_$])` guard: only alias when the RHS is the tokenData OBJECT, not a PROPERTY of
37
- // it — `const seed = abx.tokenData.seed` aliases `seed` to a scalar, so its method calls
38
- // (`seed.startsWith(…)`) must NOT be read as params (the false-positive that flagged startsWith/slice).
39
- const aliasRe = /\b([A-Za-z_$][\w$]*)\s*=\s*[^;\n]*(?:abx\s*\??\s*\.\s*tokenData|window\s*\??\s*\.\s*abxTokenData)(?!\s*\??\s*\.\s*[A-Za-z_$])/g;
40
- let m;
41
- while ((m = aliasRe.exec(source)) !== null)
42
- aliases.add(m[1]);
43
- for (const a of aliases) {
44
- // `(?!\s*\()` excludes method CALLS (`td.slice(…)`) — a param read is a bare property, never a call.
45
- const dotRe = new RegExp(`\\b${a.replace(/[$]/g, '\\$')}\\s*\\??\\s*\\.\\s*([A-Za-z_$][\\w$]*)(?!\\s*\\()`, 'g');
46
- let d;
47
- while ((d = dotRe.exec(source)) !== null)
48
- add(d[1]);
49
- }
50
- // Direct `abx.tokenData.key` and `abx.tokenData['key']`.
51
- const directRe = /abx\s*\??\s*\.\s*tokenData\s*\??\s*(?:\.\s*([A-Za-z_$][\w$]*)|\[\s*['"]([^'"]+)['"]\s*\])/g;
52
- while ((m = directRe.exec(source)) !== null)
53
- add(m[1] ?? m[2]);
54
- // Destructuring: `const { palette, foo } = abx.tokenData` (or an alias).
55
- const destrRe = /(?:const|let|var)\s*\{([^}]*)\}\s*=\s*[^;\n]*(?:abx\s*\.\s*tokenData|\b(?:tokenData|td)\b)/g;
56
- while ((m = destrRe.exec(source)) !== null) {
57
- for (const part of m[1].split(','))
58
- add(part.split(':')[0].trim().replace(/\.\.\./, '') || undefined);
59
- }
60
- return [...keys];
61
- }
62
- /** Extract the key names from the first `abx.traits({ ... })` object literal (flat objects only). */
63
- function traitKeys(source) {
64
- const m = source.match(/abx\s*\??\s*\.\s*traits\s*\(\s*\{([^}]*)\}/);
65
- if (!m)
66
- return [];
67
- const keys = [];
68
- // A key sits at the start of the object or right after a comma (so a ternary VALUE like
69
- // `? 'Sparse' : 'Dense'` — a `:` not preceded by `,`/`{` — is never mistaken for a key). Allow
70
- // leading whitespace after `^` so the FIRST key (preceded only by the newline after `{`) is caught.
71
- // A key is EITHER a quoted string (arbitrary content — `'Plant Count'`, a spaced trait_type a
72
- // marketplace shows) OR a bare identifier; a bare-identifier-only match silently dropped quoted
73
- // keys, and "believe inspect" then sent authors chasing a phantom missing trait.
74
- const re = /(?:^\s*|[,{]\s*)(?:(['"])([^'"]+)\1|([A-Za-z_$][\w$]*))\s*:/g;
75
- let k;
76
- while ((k = re.exec(m[1])) !== null)
77
- keys.push(k[2] ?? k[3]);
78
- return [...new Set(keys)];
79
- }
80
- export function analyzeScript(source, declaredDeps = []) {
81
- const bytes = new TextEncoder().encode(source).length;
82
- const keys = traitKeys(source);
83
- const usesMathRandom = /Math\s*\.\s*random\s*\(/.test(source);
84
- // p5's bare random(...) — a `random(` NOT preceded by `.` or a word char (so not Math.random / obj.random)
85
- const usesBareRandom = /(^|[^.\w])random\s*\(/.test(source);
86
- const seeded = /\brandomSeed\s*\(/.test(source);
87
- const usesNoise = /\bnoise\s*\(/.test(source);
88
- const looksP5 = /\bcreateCanvas\s*\(|function\s+setup\s*\(|function\s+draw\s*\(|\bp5\b/.test(source);
89
- const depHints = [];
90
- if (looksP5)
91
- depHints.push('p5');
92
- if (/\bTHREE\b/.test(source))
93
- depHints.push('three');
94
- if (/\bTone\b/.test(source))
95
- depHints.push('tone');
96
- // The abx.js runtime data contract — does the script read state + report traits the ONE way the
97
- // toolkit injects/captures them? A near-miss global deploys fine but is silently broken.
98
- // `\??\s*\.` tolerates optional chaining (`abx?.tokenData`) — a common, correct way to read it.
99
- const readsTokenData = /abx\s*\??\s*\.\s*tokenData\b/.test(source) || /window\s*\??\s*\.\s*abxTokenData\b/.test(source);
100
- const reportsTraits = /abx\s*\??\s*\.\s*traits\s*\(/.test(source);
101
- let wrongGlobal = null;
102
- if (!readsTokenData) {
103
- if (/window\s*\.\s*tokenTraits\b/.test(source))
104
- wrongGlobal = 'window.tokenTraits — traits are reported by CALLING abx.traits({…}), never by writing a global';
105
- else if (/window\s*\.\s*tokenData\b/.test(source))
106
- wrongGlobal = 'window.tokenData — abx injects window.abxTokenData (read it via abx.tokenData), not window.tokenData';
107
- else if (/\btokenData\b/.test(source))
108
- wrongGlobal = 'a bare `tokenData` — read abx.tokenData (via abx.js) or the raw window.abxTokenData global';
109
- }
110
- // Trait-reproducibility rubric (see docs/research/onchain-traits-feasibility.md).
111
- let verdict;
112
- let reason;
113
- if (keys.length === 0) {
114
- verdict = 'none';
115
- reason = 'no abx.traits({…}) call → NO marketplace traits on ANY lane. A resolver captures the keys you pass to abx.traits(); it does not invent traits from internal variables or a global. Call abx.traits({…}) in the sketch if you want filterable traits.';
116
- }
117
- else if (usesMathRandom && !seeded) {
118
- verdict = 'infeasible';
119
- reason = 'traits derive from Math.random() (unseeded) — non-deterministic, reproducible nowhere. Serve attributes via a resolver, or ship without marketplace traits.';
120
- }
121
- else if (seeded && looksP5) {
122
- verdict = 'exact-likely';
123
- reason = 'seeded p5 random() is a documented LCG — integer floor/threshold/select traits port EXACTLY to Solidity. Confirm no trait keys off a raw float value. Wire with --attributes-renderer (see docs/research/onchain-traits-feasibility.md).';
124
- }
125
- else if (seeded) {
126
- verdict = 'careful';
127
- reason = "seeded, but the PRNG isn't identified as p5 — reproducible on-chain if you port that generator + the exact call order.";
128
- }
129
- else if (usesBareRandom) {
130
- verdict = 'unknown';
131
- reason = 'traits present and random() is used, but no randomSeed() was found — check the art is deterministic before attempting on-chain traits.';
132
- }
133
- else {
134
- verdict = 'exact-likely';
135
- reason = 'traits look derived from the seed/params directly (no PRNG) — reproducible on-chain via an attributes field-renderer.';
136
- }
137
- const deps = declaredDeps;
138
- let unknownDepSizes = false;
139
- let depDocBytes = 0;
140
- for (const d of deps) {
141
- if (d in KNOWN_DEP_DOC_BYTES)
142
- depDocBytes += KNOWN_DEP_DOC_BYTES[d];
143
- else
144
- unknownDepSizes = true;
145
- }
146
- const estBytes = bytes + RUNTIME_DOC_BYTES + depDocBytes;
147
- return {
148
- bytes,
149
- estChunks: Math.max(1, Math.ceil(bytes / 22_000)),
150
- traits: { present: keys.length > 0, keys },
151
- paramHints: paramKeys(source),
152
- prng: { seeded, usesBareRandom, usesMathRandom, usesNoise },
153
- depHints,
154
- looksP5,
155
- feasibility: { verdict, reason },
156
- doc: { estBytes, deps, fitsSingleCall: estBytes <= SINGLE_CALL_DOC_CEILING && !unknownDepSizes, unknownDepSizes },
157
- runtime: { readsTokenData, reportsTraits, wrongGlobal },
158
- };
159
- }
160
- /** A one-line lane recommendation derived from the analysis (the decision-tree output). */
161
- export function recommendLane(a) {
162
- const bigDoc = !a.doc.fitsSingleCall && !a.doc.unknownDepSizes;
163
- if (bigDoc) {
164
- return 'DIRECTORY mode (--code-dir) or a CDN dependency — the assembled document is too large for a single tokenURI eth_call to be reliable.';
165
- }
166
- if (a.feasibility.verdict === 'infeasible') {
167
- return 'RESOLVER lane (--public-base-url) if marketplace traits matter (a server serves the JS-derived attributes), OR --onchain-uri and accept no marketplace traits (the tokenURI + animation are still fully on-chain).';
168
- }
169
- if (a.feasibility.verdict === 'exact-likely' || a.feasibility.verdict === 'none') {
170
- const dep = a.depHints.length ? ` --dep ${a.depHints[0]}@<version>` : '';
171
- const docKb = Math.round(a.doc.estBytes / 1000);
172
- // Owner call (real-world experience): for a generative drop meant to sell, LEAD with the off-chain
173
- // resolver — maneuverable over time + marketplaces fetch a SMALL tokenURI reliably. Fully-on-chain
174
- // is the durability-max alternative, but its ~docKb tokenURI (whole doc per call) strains some
175
- // marketplace/indexer reads. Traits are NOT a free flag on the on-chain lane (deployed renderer).
176
- const traits = a.traits.present
177
- ? ` Traits (${a.traits.keys.join(', ')}): the resolver serves them from the render with no Solidity; on the on-chain lane they need a DEPLOYED --attributes-renderer (fork SeedTraitsRenderer.sol) or they're omitted.`
178
- : ``;
179
- return (`RECOMMENDED for a drop you'll sell — OFF-CHAIN RESOLVER (--public-base-url + an effects runner): maneuverable (metadata/serving can evolve without on-chain surgery) and marketplaces fetch a SMALL tokenURI reliably.${traits}\n` +
180
- ` ALTERNATIVE — FULLY ON-CHAIN (--onchain-uri${dep} --image-base <bucket>): maximal durability / no server, but the tokenURI carries the whole ~${docKb}KB document per call (some marketplace + indexer reads choke on a doc this big), stills are MANUAL, and later changes are on-chain re-points. Pick it when permanence + zero-infra outweigh maneuverability.`);
181
- }
182
- return (`RECOMMENDED — an OFF-CHAIN RESOLVER (--public-base-url): maneuverable, and it serves traits from the render regardless of the PRNG. --onchain-uri also works for tokenURI + animation, but first verify the script is deterministic (see the feasibility note), and note the large-tokenURI marketplace-read tradeoff; on that lane traits need a DEPLOYED --attributes-renderer or are omitted.`);
183
- }
184
- //# sourceMappingURL=inspect.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"inspect.js","sourceRoot":"","sources":["../src/inspect.ts"],"names":[],"mappings":"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"}