@artblocks/abx-cli 0.1.0-alpha.2 → 0.1.0-alpha.21

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 (137) hide show
  1. package/CHANGELOG.md +1991 -0
  2. package/assets/renderer-scaffold/src/interfaces/IAbxParams.sol +17 -0
  3. package/assets/renderer-scaffold/test/MyRenderer.t.sol +51 -2
  4. package/dist/commands/deploy.d.ts +206 -0
  5. package/dist/commands/deploy.d.ts.map +1 -0
  6. package/dist/commands/deploy.js +4273 -0
  7. package/dist/commands/deploy.js.map +1 -0
  8. package/dist/commands/project.d.ts +241 -0
  9. package/dist/commands/project.d.ts.map +1 -0
  10. package/dist/commands/project.js +1060 -0
  11. package/dist/commands/project.js.map +1 -0
  12. package/dist/commands/reads.d.ts +64 -0
  13. package/dist/commands/reads.d.ts.map +1 -0
  14. package/dist/commands/reads.js +697 -0
  15. package/dist/commands/reads.js.map +1 -0
  16. package/dist/commands/scaffold.d.ts +71 -0
  17. package/dist/commands/scaffold.d.ts.map +1 -0
  18. package/dist/commands/scaffold.js +552 -0
  19. package/dist/commands/scaffold.js.map +1 -0
  20. package/dist/commands/service.d.ts +41 -0
  21. package/dist/commands/service.d.ts.map +1 -0
  22. package/dist/commands/service.js +618 -0
  23. package/dist/commands/service.js.map +1 -0
  24. package/dist/commands/storage.d.ts +37 -0
  25. package/dist/commands/storage.d.ts.map +1 -0
  26. package/dist/commands/storage.js +343 -0
  27. package/dist/commands/storage.js.map +1 -0
  28. package/dist/config.d.ts +91 -2
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +244 -11
  31. package/dist/config.js.map +1 -1
  32. package/dist/conformance.d.ts +31 -0
  33. package/dist/conformance.d.ts.map +1 -0
  34. package/dist/conformance.js +371 -0
  35. package/dist/conformance.js.map +1 -0
  36. package/dist/deps.d.ts +6 -39
  37. package/dist/deps.d.ts.map +1 -1
  38. package/dist/deps.js +4 -68
  39. package/dist/deps.js.map +1 -1
  40. package/dist/errors.d.ts +20 -0
  41. package/dist/errors.d.ts.map +1 -0
  42. package/dist/errors.js +25 -0
  43. package/dist/errors.js.map +1 -0
  44. package/dist/flag-allowlists.d.ts +53 -0
  45. package/dist/flag-allowlists.d.ts.map +1 -0
  46. package/dist/flag-allowlists.js +148 -0
  47. package/dist/flag-allowlists.js.map +1 -0
  48. package/dist/flags.d.ts +41 -0
  49. package/dist/flags.d.ts.map +1 -1
  50. package/dist/flags.js +94 -1
  51. package/dist/flags.js.map +1 -1
  52. package/dist/jsonout.d.ts +37 -0
  53. package/dist/jsonout.d.ts.map +1 -0
  54. package/dist/jsonout.js +68 -0
  55. package/dist/jsonout.js.map +1 -0
  56. package/dist/kind.d.ts +53 -0
  57. package/dist/kind.d.ts.map +1 -0
  58. package/dist/kind.js +94 -0
  59. package/dist/kind.js.map +1 -0
  60. package/dist/main.js +534 -4708
  61. package/dist/main.js.map +1 -1
  62. package/dist/mintpage.d.ts +17 -2
  63. package/dist/mintpage.d.ts.map +1 -1
  64. package/dist/mintpage.js +214 -52
  65. package/dist/mintpage.js.map +1 -1
  66. package/dist/output.d.ts +148 -0
  67. package/dist/output.d.ts.map +1 -0
  68. package/dist/output.js +634 -0
  69. package/dist/output.js.map +1 -0
  70. package/dist/ownerops.d.ts +132 -44
  71. package/dist/ownerops.d.ts.map +1 -1
  72. package/dist/ownerops.js +1151 -329
  73. package/dist/ownerops.js.map +1 -1
  74. package/dist/preview.d.ts +81 -0
  75. package/dist/preview.d.ts.map +1 -0
  76. package/dist/preview.js +535 -0
  77. package/dist/preview.js.map +1 -0
  78. package/dist/prompt.d.ts +17 -0
  79. package/dist/prompt.d.ts.map +1 -0
  80. package/dist/prompt.js +19 -0
  81. package/dist/prompt.js.map +1 -0
  82. package/dist/provision.d.ts.map +1 -1
  83. package/dist/provision.js +10 -7
  84. package/dist/provision.js.map +1 -1
  85. package/dist/remote.d.ts +134 -53
  86. package/dist/remote.d.ts.map +1 -1
  87. package/dist/remote.js +358 -46
  88. package/dist/remote.js.map +1 -1
  89. package/dist/riskgate.d.ts +29 -0
  90. package/dist/riskgate.d.ts.map +1 -0
  91. package/dist/riskgate.js +91 -0
  92. package/dist/riskgate.js.map +1 -0
  93. package/dist/scaffold.d.ts +10 -0
  94. package/dist/scaffold.d.ts.map +1 -0
  95. package/dist/scaffold.js +52 -0
  96. package/dist/scaffold.js.map +1 -0
  97. package/dist/schema.d.ts +18 -1
  98. package/dist/schema.d.ts.map +1 -1
  99. package/dist/schema.js +74 -23
  100. package/dist/schema.js.map +1 -1
  101. package/dist/served.d.ts +30 -0
  102. package/dist/served.d.ts.map +1 -0
  103. package/dist/served.js +112 -0
  104. package/dist/served.js.map +1 -0
  105. package/dist/signer.d.ts +13 -0
  106. package/dist/signer.d.ts.map +1 -1
  107. package/dist/signer.js +80 -14
  108. package/dist/signer.js.map +1 -1
  109. package/dist/update-check.d.ts +83 -4
  110. package/dist/update-check.d.ts.map +1 -1
  111. package/dist/update-check.js +132 -17
  112. package/dist/update-check.js.map +1 -1
  113. package/package.json +10 -6
  114. package/skill/SKILL.md +108 -62
  115. package/skill/reference/code-projects.md +109 -13
  116. package/skill/reference/creator-token.md +86 -0
  117. package/skill/reference/decisions.md +87 -0
  118. package/skill/reference/hosting.md +48 -15
  119. package/skill/reference/operating.md +32 -8
  120. package/skill/reference/setup.md +10 -3
  121. package/skill/reference/troubleshooting.md +31 -4
  122. package/dist/inspect.d.ts +0 -48
  123. package/dist/inspect.d.ts.map +0 -1
  124. package/dist/inspect.js +0 -184
  125. package/dist/inspect.js.map +0 -1
  126. package/dist/migrate.d.ts +0 -65
  127. package/dist/migrate.d.ts.map +0 -1
  128. package/dist/migrate.js +0 -180
  129. package/dist/migrate.js.map +0 -1
  130. package/dist/onchain-uri.d.ts +0 -97
  131. package/dist/onchain-uri.d.ts.map +0 -1
  132. package/dist/onchain-uri.js +0 -243
  133. package/dist/onchain-uri.js.map +0 -1
  134. package/dist/upload.d.ts +0 -28
  135. package/dist/upload.d.ts.map +0 -1
  136. package/dist/upload.js +0 -41
  137. package/dist/upload.js.map +0 -1
@@ -16,9 +16,10 @@ After launch the owner operates the project. Each command builds a tx, signs it
16
16
  | `abx set-contract-uri <addr> (--uri <base> \| --override <uri>)` | re-point the collection base, or pin a fixed locator | the new base / locator |
17
17
  | `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
18
  | `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 <resolver>]` | render missing stills/traits now (repair lane); `--remote` publishes to a HOSTED resolver | none — idempotent; local captures need Playwright chromium |
20
- | `abx add <addr> --remote [url]` · `abx index <addr> --remote` · `abx forget <addr> --remote` | register / re-index (nudge) / deregister a contract on a HOSTED resolver (remote control plane) | needs `ABX_RESOLVER_ADMIN_TOKEN`; url defaults to `ABX_PUBLIC_BASE_URL` |
21
- | `abx migrate <addr> --from <src-url> --to <dest-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 |
19
+ | `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 |
20
+ | `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 |
21
+ | `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>` |
22
+ | `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
23
  | `abx set-royalty <addr> --bps <0-10000> [--receiver 0x..]` | change the royalty (receiver defaults to current) | the rate, and whether the payee changes |
23
24
  | `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
25
  | `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 |
@@ -27,12 +28,11 @@ After launch the owner operates the project. Each command builds a tx, signs it
27
28
 
28
29
  **`lock-field` / `lock-uri` are permanent — always confirm with the human first.**
29
30
 
30
- ### Authorship + rights (credit + license)
31
+ **Welding a PARAM is a different verb.** Fields and [PostParams](code-projects.md#postparams) are separate namespaces that can share a name, and `lock-field` freezes the *field*. To make a param's value permanent, lock its schema: `abx set-schema <addr> --schema <key>:<Type>:<Auth>:lock=now` — after which every `configure-param` on that key reverts. (`lock-field` refuses a declared param key rather than letting you lock the wrong thing.)
31
32
 
32
- The reserved **collection** keys are `artist`, `display_notes`, `artist_links`, `license` (a rights *identifier* like `CC0-1.0` / `CC-BY-NC-4.0`). Two ways to set them, both writing on-chain inline collection fields:
33
+ ### Authorship + rights (credit + license)
33
34
 
34
- - **At deploy** (all three deploy commands): `abx deploy --artist "Casey Reas" --license "CC BY-NC 4.0"` (also `--display-notes`, `--artist-links`)baked into the deploy tx, parity with `--description`/`--external-url`.
35
- - **Later / to change**: `abx set-field <addr> --collection --field artist --text "Casey Reas"`.
35
+ The reserved **collection** keys are `artist`, `display_notes`, `artist_links`, `license` (a rights *identifier* like `CC0-1.0` / `CC-BY-NC-4.0`). `--artist`/`--license`/`--display-notes`/`--artist-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 artist --text "Casey Reas"` directly.
36
36
 
37
37
  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
38
 
@@ -48,7 +48,7 @@ A token anchors **named, typed files**; the served JSON's **`artifacts`** array
48
48
  - scope: `--token <id>` (default 0) or `--collection`; any lane; `--dry-run` previews the tx.
49
49
  - **`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
50
  - **Verify** — a resolver serves the complete listing: `curl <resolver>/t/<chainId>/<addr>/<id>` shows the `artifacts` array, and `/t/<chainId>/<addr>/<id>/data/<key>` fetches (or 302-redirects to) each file. The attached bytes are stored on-chain + keccak-anchored either way.
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 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.
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 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, so a bare `tokenURI` emits every set param under **`abx_params`** with no resolver in the picture. Say it to a creator as one line — *attachments always need a resolver; params never do.*
52
52
 
53
53
  ### Series-only owner ops
54
54
 
@@ -65,11 +65,31 @@ For a multi-token Series ([SKILL → Series](../SKILL.md#series-multi-token-drop
65
65
 
66
66
  `set-field` / `lock-field` work per **token** for a Series — pass the token id (`--field image --file … ` targets that token's artwork); tokens share the collection scope with `--collection`.
67
67
 
68
+ ### Edition owner ops
69
+
70
+ 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 artwork (transfer, royalty, fields, locking); these are the edition-only twins, keyed by **id** instead of a single owner.
71
+
72
+ | Command | Does | Decision to surface |
73
+ |---|---|---|
74
+ | `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 artworks; see `abx tokens <addr>`) | which id, how many copies, who receives them |
75
+ | `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 |
76
+ | `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 |
77
+ | `abx ping-uri <addr> --token-ids <csv\|range>` | permissionless: re-emit the native ERC-1155 `URI` event 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 | none — run it when a marketplace/indexer only honors the native per-id event and missed the re-point |
78
+ | `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 |
79
+ | `abx minter buy <addr> --token-id <n> --quantity <n>` | buy `n` copies of that id in one purchase — pays `price × quantity` | how many copies |
80
+ | `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 |
81
+
82
+ `--token-id` is required on every edition op above that names a specific artwork, 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.
83
+
84
+ **Two numbers, not one — get this straight with the creator BEFORE deploying.** An edition has an **id space** (how many distinct artworks) 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** artwork: 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).
85
+
68
86
  ### Selling — the shared fixed-price minter
69
87
 
70
88
  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
89
 
72
90
  > **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.
91
+ >
92
+ > **The edition minter is the one exception — it DOES sell a "1/1"-shaped artwork directly.** `AbxFixedPriceMinter1155` targets `IAbxEditionMint`, which `OneOfOneEdition` implements (id space fixed to `0`), so a priced open/limited edition of a single artwork 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
93
 
74
94
  | Command | Does | Notes |
75
95
  |---|---|---|
@@ -86,6 +106,8 @@ abx unpause <token> # open the sale (th
86
106
  ```
87
107
  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
108
 
109
+ **On an edition, only the minter calls need `--token-id`** — `minter configure/show/buy --token-id <n>` (a different sale per artwork, 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`.
110
+
89
111
  ### A mint website for buyers — `abx mint-page`
90
112
 
91
113
  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 +118,7 @@ Once a sale is live, `abx mint-page <token>` scaffolds a **self-contained Next.j
96
118
  | 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
119
 
98
120
  - **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.
121
+ - **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 artwork, instead.
99
122
  - **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
123
  - **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
124
 
@@ -103,6 +126,7 @@ Once a sale is live, `abx mint-page <token>` scaffolds a **self-contained Next.j
103
126
 
104
127
  - **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
128
  - **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**.
129
+ - **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
130
 
107
131
  ### The cutover, after a clean `migrate`
108
132
 
@@ -13,18 +13,21 @@ When `abx doctor` flags a tight range, or a reconstruction would be large/slow
13
13
 
14
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
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
+
16
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.
17
19
 
18
20
  ## Troubleshooting
19
21
 
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 `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).
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 `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
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.
22
24
  - **storage 'cloud' missing accessKeyId/…** → set `ABX_S3_*` in `.env`. **'ipfs' unreachable** → Kubo not running or `PINATA_JWT` missing.
23
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.
24
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":
25
- - **A hosted resolver you just registered** (`abx add … --remote`): it may still be **backfilling** — hit `GET /` (or `abx status --remote`) 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.)
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.)
26
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.
27
- - **`/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.)
29
+ - **`/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.
30
+ - **`/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
31
  - **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
32
  - **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
33
  - **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.
@@ -32,5 +35,9 @@ When `abx doctor` flags a tight range, or a reconstruction would be large/slow
32
35
  - **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
36
  - **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
37
  - **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.
38
+ - **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`).
39
+ - **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>`).
40
+ - **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.
41
+ - **`/.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
42
  - **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
43
  - **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
- Note: `abx state` / `abx tokenuri` are **pure RPC reads** they work on any address with no local setup. `abx verify` / `abx index` / data-plane ops need the project **registered on this node first** (`abx add <addr>`); their error says so.
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:
@@ -16,13 +18,38 @@ Walk the cause down:
16
18
  ### I changed the metadata but the marketplace shows the old value
17
19
  - **First confirm the change landed:** `abx tokenuri <addr> --token N` (or the resolver's JSON) — if it shows the NEW value with `abx_provenance` `onChain: true`, the write worked. The gap is 100% the marketplace's cache.
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
- - **Hosted resolver?** It serves from its own store — a local edit must reach it: `abx add <addr> --remote <url>` / `abx index <addr> --remote <url>` re-indexes the hosted node. Never "resubmit the transaction" or redeploy.
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
+ `abx refresh` and ERC-4906 are 721-only — an edition (`OneOfOneEdition`/`EditionImage`/`EditionCode`)
25
+ signals a metadata change with ERC-1155's native `URI(string,uint256)` event instead. A contract-wide
26
+ re-point (`abx set-token-uri`/`abx set-renderer`) only emits its own config event, not a per-id `URI` —
27
+ so a marketplace/indexer that honors *only* the native per-id event misses it. Fix: `abx ping-uri <addr>
28
+ --token-ids <csv|range>` re-emits `URI` for the ids you name — permissionless, no owner check needed.
29
+
30
+ ### "It's registered but serves nothing" / the add seemed to hang
31
+ **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.
32
+
33
+ **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
34
 
21
35
  ### `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 node without the cap.
36
+ 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
37
 
24
38
  ### The on-chain tokenURI points at `localhost`
25
39
  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
40
 
41
+ ### `deploy-code` deployed the contract but the SETUP transaction failed
42
+ 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.
43
+
44
+ **The contract is recoverable. Do NOT redeploy, and do NOT hand-assemble a multicall with `cast`.** Finish it:
45
+
46
+ ```bash
47
+ abx deploy-code --resume <address> <the SAME content flags the original deploy used> # add --dry-run first
48
+ ```
49
+
50
+ 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.
51
+
52
+ If the DEPLOY (first) transaction is what failed, there is nothing to resume — no contract exists. Run a normal deploy.
53
+
27
54
  ### `abx index`/`abx verify` says "isn't registered"
28
55
  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
@@ -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":"AAAA;;;;;;;;;;GAUG;AAEH;oGACoG;AACpG,MAAM,mBAAmB,GAA2B;IAClD,UAAU,EAAE,OAAO,EAAE,iEAAiE;CACvF,CAAC;AAEF,mGAAmG;AACnG,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAEjC;;kGAEkG;AAClG,MAAM,uBAAuB,GAAG,SAAS,CAAC;AAsB1C;;;;;qGAKqG;AACrG,SAAS,SAAS,CAAC,MAAc;IAC/B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAG,CAAC,CAAqB,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,MAAM;QAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,gGAAgG;IAChG,+FAA+F;IAC/F,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,oGAAoG;IACpG,yFAAyF;IACzF,wGAAwG;IACxG,MAAM,OAAO,GAAG,gIAAgI,CAAC;IACjJ,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,qGAAqG;QACrG,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,mDAAmD,EAAE,GAAG,CAAC,CAAC;QACjH,IAAI,CAAyB,CAAC;QAC9B,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI;YAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC;IACD,yDAAyD;IACzD,MAAM,QAAQ,GAAG,4FAA4F,CAAC;IAC9G,OAAO,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI;QAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,yEAAyE;IACzE,MAAM,OAAO,GAAG,6FAA6F,CAAC;IAC9G,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AACnB,CAAC;AAED,qGAAqG;AACrG,SAAS,SAAS,CAAC,MAAc;IAC/B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;IACrE,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,wFAAwF;IACxF,+FAA+F;IAC/F,oGAAoG;IACpG,8FAA8F;IAC9F,gGAAgG;IAChG,iFAAiF;IACjF,MAAM,EAAE,GAAG,8DAA8D,CAAC;IAC1E,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,eAAyB,EAAE;IACvE,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IACtD,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,cAAc,GAAG,yBAAyB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9D,2GAA2G;IAC3G,MAAM,cAAc,GAAG,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,uEAAuE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrG,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,OAAO;QAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrD,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEnD,gGAAgG;IAChG,yFAAyF;IACzF,gGAAgG;IAChG,MAAM,cAAc,GAAG,8BAA8B,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,oCAAoC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxH,MAAM,aAAa,GAAG,8BAA8B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClE,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,IAAI,6BAA6B,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,WAAW,GAAG,gGAAgG,CAAC;aAC1J,IAAI,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,WAAW,GAAG,sGAAsG,CAAC;aACnK,IAAI,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,WAAW,GAAG,4FAA4F,CAAC;IACpJ,CAAC;IAED,kFAAkF;IAClF,IAAI,OAAyB,CAAC;IAC9B,IAAI,MAAc,CAAC;IACnB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,GAAG,MAAM,CAAC;QACjB,MAAM,GAAG,sPAAsP,CAAC;IAClQ,CAAC;SAAM,IAAI,cAAc,IAAI,CAAC,MAAM,EAAE,CAAC;QACrC,OAAO,GAAG,YAAY,CAAC;QACvB,MAAM,GAAG,6JAA6J,CAAC;IACzK,CAAC;SAAM,IAAI,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,OAAO,GAAG,cAAc,CAAC;QACzB,MAAM,GAAG,0OAA0O,CAAC;IACtP,CAAC;SAAM,IAAI,MAAM,EAAE,CAAC;QAClB,OAAO,GAAG,SAAS,CAAC;QACpB,MAAM,GAAG,wHAAwH,CAAC;IACpI,CAAC;SAAM,IAAI,cAAc,EAAE,CAAC;QAC1B,OAAO,GAAG,SAAS,CAAC;QACpB,MAAM,GAAG,wIAAwI,CAAC;IACpJ,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,cAAc,CAAC;QACzB,MAAM,GAAG,uHAAuH,CAAC;IACnI,CAAC;IAED,MAAM,IAAI,GAAG,YAAY,CAAC;IAC1B,IAAI,eAAe,GAAG,KAAK,CAAC;IAC5B,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,mBAAmB;YAAE,WAAW,IAAI,mBAAmB,CAAC,CAAC,CAAC,CAAC;;YAC/D,eAAe,GAAG,IAAI,CAAC;IAC9B,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,GAAG,iBAAiB,GAAG,WAAW,CAAC;IACzD,OAAO;QACL,KAAK;QACL,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC;QACjD,MAAM,EAAE,EAAC,OAAO,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,EAAC;QACxC,UAAU,EAAE,SAAS,CAAC,MAAM,CAAC;QAC7B,IAAI,EAAE,EAAC,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,SAAS,EAAC;QACzD,QAAQ;QACR,OAAO;QACP,WAAW,EAAE,EAAC,OAAO,EAAE,MAAM,EAAC;QAC9B,GAAG,EAAE,EAAC,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,IAAI,uBAAuB,IAAI,CAAC,eAAe,EAAE,eAAe,EAAC;QAC/G,OAAO,EAAE,EAAC,cAAc,EAAE,aAAa,EAAE,WAAW,EAAC;KACtD,CAAC;AACJ,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,aAAa,CAAC,CAAiB;IAC7C,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC;IAC/D,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,sIAAsI,CAAC;IAChJ,CAAC;IACD,IAAI,CAAC,CAAC,WAAW,CAAC,OAAO,KAAK,YAAY,EAAE,CAAC;QAC3C,OAAO,oNAAoN,CAAC;IAC9N,CAAC;IACD,IAAI,CAAC,CAAC,WAAW,CAAC,OAAO,KAAK,cAAc,IAAI,CAAC,CAAC,WAAW,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;QACjF,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;QAChD,mGAAmG;QACnG,mGAAmG;QACnG,+FAA+F;QAC/F,kGAAkG;QAClG,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO;YAC7B,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,iLAAiL;YACvN,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,CACL,yNAAyN,MAAM,IAAI;YACnO,gDAAgD,GAAG,gGAAgG,KAAK,8MAA8M,CACvW,CAAC;IACJ,CAAC;IACD,OAAO,CACL,kYAAkY,CACnY,CAAC;AACJ,CAAC"}
package/dist/migrate.d.ts DELETED
@@ -1,65 +0,0 @@
1
- import type { OpenSeaAttribute, ProjectState } from '@artblocks/abx-sdk';
2
- import { type StorageBackend } from '@artblocks/abx-storage';
3
- /** An image that lives ONLY on the source host (committed by hash, never pinned to a durable
4
- * backend) — there's no portable URL to copy, so its bytes must be re-pinned before cutover. */
5
- export interface NodeCustodyImage {
6
- tokenId: string;
7
- /** On-chain content hash (the `keccak256`/`sha256` commitment) — what we verify re-pinned bytes against. */
8
- hash: string;
9
- /** The source's served image URL — the old host's own `/…/image` route; where we fetch the bytes. */
10
- imageUrl: string;
11
- }
12
- /** The off-chain state to bridge to the destination resolver + what couldn't be carried cleanly. */
13
- export interface MigrationPlan {
14
- description?: string;
15
- externalUrl?: string;
16
- attributes?: OpenSeaAttribute[];
17
- /** `{ "0x<keccak>": "https://gw/ipfs/<cid>" }` — durable image locators recovered from the source. */
18
- contentLocators: Record<string, string>;
19
- /** Images served only by the source host (no durable locator) — re-pin before cutover. */
20
- nodeCustody: NodeCustodyImage[];
21
- /** Tokens whose served JSON we read to build this plan. */
22
- tokensRead: number;
23
- }
24
- export declare const tokenApiUrl: (base: string, chainId: number, addr: string, id: string) => string;
25
- export declare const contractApiUrl: (base: string, chainId: number, addr: string) => string;
26
- /**
27
- * Build the migration plan by reading the SOURCE resolver's public API, using the locally
28
- * reconstructed on-chain state (`local`) for the hash KEYS the served JSON doesn't expose.
29
- */
30
- export declare function buildMigrationPlan(sourceBase: string, chainId: number, local: ProjectState): Promise<MigrationPlan>;
31
- /**
32
- * Re-pin node-custody images to a DURABLE backend so the destination never depends on the
33
- * (soon-to-be-decommissioned) source host. For each image: fetch the bytes from the source,
34
- * **verify** them against the on-chain hash (trustless — we trust the chain, not the old node),
35
- * then `put` them to the destination operator's own backend and read back a portable locator.
36
- *
37
- * If the configured backend isn't durable (no `locator` — fs/cloud serve only through their own
38
- * node, so re-pinning there just moves the single-host dependency), we re-pin NOTHING and report
39
- * every image as `needsDurableBackend`: the right move is to configure IPFS/Arweave and re-run,
40
- * never to bridge a URL that points back at the dying host.
41
- */
42
- export interface RepinResult {
43
- /** hash → new durable locator, ready to bridge to the destination. */
44
- repinned: Record<string, string>;
45
- /** Re-pinned token ids (for reporting). */
46
- repinnedTokens: string[];
47
- /** Bytes fetched but their hash didn't match the on-chain commitment — NOT trusted, NOT pinned. */
48
- mismatched: NodeCustodyImage[];
49
- /** Couldn't fetch the bytes from the source (host down, 404). */
50
- unreachable: NodeCustodyImage[];
51
- /** Skipped because no durable backend is configured — these need IPFS/Arweave + a re-run. */
52
- needsDurableBackend: NodeCustodyImage[];
53
- }
54
- export declare function repinNodeCustody(images: NodeCustodyImage[], backend: StorageBackend): Promise<RepinResult>;
55
- /** A parity check after bridging: does the destination now serve the same image/description/traits? */
56
- export interface ParityResult {
57
- tokenId: string;
58
- imageMatch: boolean;
59
- descriptionMatch: boolean;
60
- attributesMatch: boolean;
61
- sourceImage?: string;
62
- destImage?: string;
63
- }
64
- export declare function verifyParity(sourceBase: string, destBase: string, chainId: number, addr: string, tokenId: string): Promise<ParityResult>;
65
- //# sourceMappingURL=migrate.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAqB,gBAAgB,EAAE,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAC3F,OAAO,EAAc,KAAK,cAAc,EAAC,MAAM,wBAAwB,CAAC;AA4BxE;iGACiG;AACjG,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,4GAA4G;IAC5G,IAAI,EAAE,MAAM,CAAC;IACb,qGAAqG;IACrG,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,oGAAoG;AACpG,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,sGAAsG;IACtG,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,0FAA0F;IAC1F,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;CACpB;AAGD,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,SAAS,MAAM,EAAE,MAAM,MAAM,EAAE,IAAI,MAAM,KAAG,MAC5B,CAAC;AAC3D,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,EAAE,SAAS,MAAM,EAAE,MAAM,MAAM,KAAG,MACzB,CAAC;AA+BrD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,aAAa,CAAC,CAsDxB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,2CAA2C;IAC3C,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,mGAAmG;IACnG,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,iEAAiE;IACjE,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,6FAA6F;IAC7F,mBAAmB,EAAE,gBAAgB,EAAE,CAAC;CACzC;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAyChH;AAED,uGAAuG;AACvG,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAWD,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CAavB"}