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

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 (133) hide show
  1. package/CHANGELOG.md +1762 -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 +4004 -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 +1044 -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 +667 -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 +544 -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 +335 -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/flags.d.ts +41 -0
  45. package/dist/flags.d.ts.map +1 -1
  46. package/dist/flags.js +94 -1
  47. package/dist/flags.js.map +1 -1
  48. package/dist/jsonout.d.ts +37 -0
  49. package/dist/jsonout.d.ts.map +1 -0
  50. package/dist/jsonout.js +68 -0
  51. package/dist/jsonout.js.map +1 -0
  52. package/dist/kind.d.ts +48 -0
  53. package/dist/kind.d.ts.map +1 -0
  54. package/dist/kind.js +69 -0
  55. package/dist/kind.js.map +1 -0
  56. package/dist/main.js +509 -4708
  57. package/dist/main.js.map +1 -1
  58. package/dist/mintpage.d.ts +17 -2
  59. package/dist/mintpage.d.ts.map +1 -1
  60. package/dist/mintpage.js +214 -52
  61. package/dist/mintpage.js.map +1 -1
  62. package/dist/output.d.ts +148 -0
  63. package/dist/output.d.ts.map +1 -0
  64. package/dist/output.js +634 -0
  65. package/dist/output.js.map +1 -0
  66. package/dist/ownerops.d.ts +132 -44
  67. package/dist/ownerops.d.ts.map +1 -1
  68. package/dist/ownerops.js +1067 -327
  69. package/dist/ownerops.js.map +1 -1
  70. package/dist/preview.d.ts +81 -0
  71. package/dist/preview.d.ts.map +1 -0
  72. package/dist/preview.js +535 -0
  73. package/dist/preview.js.map +1 -0
  74. package/dist/prompt.d.ts +17 -0
  75. package/dist/prompt.d.ts.map +1 -0
  76. package/dist/prompt.js +19 -0
  77. package/dist/prompt.js.map +1 -0
  78. package/dist/provision.d.ts.map +1 -1
  79. package/dist/provision.js +10 -7
  80. package/dist/provision.js.map +1 -1
  81. package/dist/remote.d.ts +134 -53
  82. package/dist/remote.d.ts.map +1 -1
  83. package/dist/remote.js +352 -46
  84. package/dist/remote.js.map +1 -1
  85. package/dist/riskgate.d.ts +29 -0
  86. package/dist/riskgate.d.ts.map +1 -0
  87. package/dist/riskgate.js +91 -0
  88. package/dist/riskgate.js.map +1 -0
  89. package/dist/scaffold.d.ts +10 -0
  90. package/dist/scaffold.d.ts.map +1 -0
  91. package/dist/scaffold.js +52 -0
  92. package/dist/scaffold.js.map +1 -0
  93. package/dist/schema.d.ts +18 -1
  94. package/dist/schema.d.ts.map +1 -1
  95. package/dist/schema.js +74 -23
  96. package/dist/schema.js.map +1 -1
  97. package/dist/served.d.ts +30 -0
  98. package/dist/served.d.ts.map +1 -0
  99. package/dist/served.js +112 -0
  100. package/dist/served.js.map +1 -0
  101. package/dist/signer.d.ts +13 -0
  102. package/dist/signer.d.ts.map +1 -1
  103. package/dist/signer.js +80 -14
  104. package/dist/signer.js.map +1 -1
  105. package/dist/update-check.d.ts +58 -4
  106. package/dist/update-check.d.ts.map +1 -1
  107. package/dist/update-check.js +92 -11
  108. package/dist/update-check.js.map +1 -1
  109. package/package.json +10 -6
  110. package/skill/SKILL.md +104 -60
  111. package/skill/reference/code-projects.md +87 -13
  112. package/skill/reference/creator-token.md +86 -0
  113. package/skill/reference/decisions.md +61 -0
  114. package/skill/reference/hosting.md +48 -15
  115. package/skill/reference/operating.md +29 -8
  116. package/skill/reference/setup.md +10 -3
  117. package/skill/reference/troubleshooting.md +31 -4
  118. package/dist/inspect.d.ts +0 -48
  119. package/dist/inspect.d.ts.map +0 -1
  120. package/dist/inspect.js +0 -184
  121. package/dist/inspect.js.map +0 -1
  122. package/dist/migrate.d.ts +0 -65
  123. package/dist/migrate.d.ts.map +0 -1
  124. package/dist/migrate.js +0 -180
  125. package/dist/migrate.js.map +0 -1
  126. package/dist/onchain-uri.d.ts +0 -97
  127. package/dist/onchain-uri.d.ts.map +0 -1
  128. package/dist/onchain-uri.js +0 -243
  129. package/dist/onchain-uri.js.map +0 -1
  130. package/dist/upload.d.ts +0 -28
  131. package/dist/upload.d.ts.map +0 -1
  132. package/dist/upload.js +0 -41
  133. package/dist/upload.js.map +0 -1
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Copying the bundled renderer scaffold onto a creator's disk.
3
+ *
4
+ * This lives in its own module because the inline version of it shipped broken for six releases
5
+ * (alpha.9 → alpha.14) and could not be reproduced in a dev checkout. `abx scaffold-renderer`
6
+ * created the directory, printed the whole success walkthrough, exited 0, and wrote **zero files** —
7
+ * for every user, on every invocation form, while working perfectly for us.
8
+ *
9
+ * The cause was a path filter that judged absolute paths:
10
+ *
11
+ * filter: (s) => !/(^|\/)(out|cache|dependencies|broadcast|node_modules)(\/|$)/.test(s)
12
+ *
13
+ * `cpSync` hands `filter` ABSOLUTE source paths. An installed CLI lives at
14
+ * `…/node_modules/@artblocks/abx-cli/assets/renderer-scaffold`, so that pattern matched the source
15
+ * ROOT — `cpSync` skips a directory's entire subtree when the directory itself is filtered out, and
16
+ * silently copies nothing rather than erroring. A dev checkout's path
17
+ * (`…/plan/abx/packages/cli/assets/…`) contains no `node_modules`, so every test we ran passed.
18
+ *
19
+ * Two rules follow, and the tests in `test/scaffold.test.ts` pin both:
20
+ * 1. Judge paths RELATIVE to the source root, never the absolute string.
21
+ * 2. Never report success without asserting the output actually landed.
22
+ */
23
+ import { cpSync, existsSync, mkdirSync } from 'node:fs';
24
+ import { join, relative } from 'node:path';
25
+ /** Build/dependency dirs that must never ride along into a fresh scaffold. Matched against the
26
+ * path RELATIVE to the scaffold root — see the module note. */
27
+ const SKIP_DIR = /^(out|cache|dependencies|broadcast|node_modules)(\/|$)/;
28
+ /** The file whose presence means the copy genuinely happened (not just `mkdir`). */
29
+ export const SCAFFOLD_ANCHOR = join('src', 'MyRenderer.sol');
30
+ /**
31
+ * Copy the scaffold at `src` into `dir`, omitting build/dependency dirs.
32
+ *
33
+ * Throws if the copy produced no files — the caller must not print a success banner over an empty
34
+ * directory. Returns nothing on success; the anchor file is guaranteed to exist.
35
+ */
36
+ export function copyRendererScaffold(src, dir) {
37
+ mkdirSync(dir, { recursive: true });
38
+ cpSync(src, dir, {
39
+ recursive: true,
40
+ filter: (s) => {
41
+ // Normalise separators so the same pattern holds on Windows.
42
+ const rel = relative(src, s).split(/[/\\]/).join('/');
43
+ return rel === '' || !SKIP_DIR.test(rel);
44
+ },
45
+ });
46
+ if (!existsSync(join(dir, SCAFFOLD_ANCHOR))) {
47
+ throw new Error(`scaffold copy wrote no files into ${dir} (expected ${SCAFFOLD_ANCHOR}) — nothing was written, ` +
48
+ `so don't build here. Scaffold source: ${src}. ` +
49
+ `Please report this along with your install path: https://abx.docs.artblocks.io`);
50
+ }
51
+ }
52
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AACtD,OAAO,EAAC,IAAI,EAAE,QAAQ,EAAC,MAAM,WAAW,CAAC;AAEzC;gEACgE;AAChE,MAAM,QAAQ,GAAG,wDAAwD,CAAC;AAE1E,oFAAoF;AACpF,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW,EAAE,GAAW;IAC3D,SAAS,CAAC,GAAG,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE;QACf,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YACZ,6DAA6D;YAC7D,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtD,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3C,CAAC;KACF,CAAC,CAAC;IACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACb,qCAAqC,GAAG,cAAc,eAAe,2BAA2B;YAC9F,yCAAyC,GAAG,IAAI;YAChD,gFAAgF,CACnF,CAAC;IACJ,CAAC;AACH,CAAC"}
package/dist/schema.d.ts CHANGED
@@ -1,12 +1,29 @@
1
- import type { Hex } from 'viem';
1
+ import type { Address, Hex } from 'viem';
2
+ /** The `--schema` type/auth/format catalog, shown wherever the CLI nudges `--schema`. Kept accurate
3
+ * to the on-chain enums (PARAM_TYPES / AUTH_OPTIONS) + the Select-options / Range-bounds format —
4
+ * the old hint advertised non-existent `Anyone`/`Admin` auth legs. Used by both `deploy-code`
5
+ * (commands/deploy.ts) and `inspect` (commands/reads.ts), so it lives here beside the rest of the
6
+ * `--schema` machinery rather than in either command module. */
7
+ export declare const SCHEMA_CATALOG: string;
2
8
  export interface ParsedSchema {
3
9
  key: string;
4
10
  paramType: number;
5
11
  auth: number;
12
+ /** Required by (and only valid for) an Address-bearing auth leg; the contract pairs them strictly. */
13
+ authAddress: Address;
14
+ /** Unix seconds after which the VALUE can no longer change. 0 = never locks. A timestamp in the
15
+ * past locks it immediately and permanently — that is the supported way to retire a parameter. */
16
+ lockAfter: number;
6
17
  min: Hex;
7
18
  max: Hex;
8
19
  selectOptions: string[];
9
20
  }
21
+ /**
22
+ * A point in time for `lock=`: unix seconds, an ISO date, or `now` (which means "immediately and
23
+ * forever" — the retire idiom). Reuses the Timestamp encoder so `lock=2026-12-31` parses exactly
24
+ * like a `Timestamp[..]` bound does; one date grammar for the whole flag.
25
+ */
26
+ export declare function parseWhen(raw: string): number;
10
27
  export declare function parseSchemaSpec(spec: string): ParsedSchema;
11
28
  /** Parse a full `--schema` value: comma-separated specs. */
12
29
  export declare function parseSchemaSpecs(raw: string | undefined): ParsedSchema[];
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,MAAM,CAAC;AAO9B,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,CAAC;IACT,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAQD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAyD1D;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,EAAE,CAMxE;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAWtD"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAC,OAAO,EAAE,GAAG,EAAC,MAAM,MAAM,CAAC;AAIvC;;;;iEAIiE;AACjE,eAAO,MAAM,cAAc,QAI+B,CAAC;AAK3D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,sGAAsG;IACtG,WAAW,EAAE,OAAO,CAAC;IACrB;uGACmG;IACnG,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,CAAC;IACT,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAID;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAU7C;AAQD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CA2F1D;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,EAAE,CAMxE;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAatD"}
package/dist/schema.js CHANGED
@@ -19,10 +19,40 @@
19
19
  * outer delimiters appear there. Omit the bracket on a Range → unbounded (min=max=0, the prior
20
20
  * behavior). A Select with no bracket, or a bracket on a type that takes none, is a clear error.
21
21
  */
22
- import { PARAM_TYPES, AUTH_OPTIONS, encodeScalarParam } from '@artblocks/abx-sdk';
22
+ import { PARAM_TYPES, AUTH_OPTIONS, decodeScalarParam, encodeScalarParam } from '@artblocks/abx-sdk';
23
23
  const ZERO32 = `0x${'0'.repeat(64)}`;
24
+ /** The `--schema` type/auth/format catalog, shown wherever the CLI nudges `--schema`. Kept accurate
25
+ * to the on-chain enums (PARAM_TYPES / AUTH_OPTIONS) + the Select-options / Range-bounds format —
26
+ * the old hint advertised non-existent `Anyone`/`Admin` auth legs. Used by both `deploy-code`
27
+ * (commands/deploy.ts) and `inspect` (commands/reads.ts), so it lives here beside the rest of the
28
+ * `--schema` machinery rather than in either command module. */
29
+ export const SCHEMA_CATALOG = 'Types: Bool·Select·Uint256Range·Int256Range·DecimalRange·HexColor·Timestamp·String·Bytes; ' +
30
+ 'Auth: Artist·TokenOwner·Address (or the Or-combos, e.g. ArtistOrTokenOwner). ' +
31
+ 'Select needs options — key:Select[A|B|C]:Auth; a Range takes bounds — key:Uint256Range[0..100]:Auth. ' +
32
+ 'A palette collectors set = palette:HexColor:TokenOwner';
24
33
  /** The types that accept a `[min..max]` bound (the contract's min/max fields are meaningful here). */
25
34
  const BOUNDED_TYPES = new Set(['Uint256Range', 'Int256Range', 'DecimalRange', 'Timestamp']);
35
+ const ZERO_ADDR = '0x0000000000000000000000000000000000000000';
36
+ /**
37
+ * A point in time for `lock=`: unix seconds, an ISO date, or `now` (which means "immediately and
38
+ * forever" — the retire idiom). Reuses the Timestamp encoder so `lock=2026-12-31` parses exactly
39
+ * like a `Timestamp[..]` bound does; one date grammar for the whole flag.
40
+ */
41
+ export function parseWhen(raw) {
42
+ const t = raw.trim();
43
+ if (t === '')
44
+ throw new Error('lock= needs a value (unix seconds, an ISO date, or `now`)');
45
+ // `now` locks on the next block. Anything already past is equally permanent — the contract's test
46
+ // is `block.timestamp > lockAfter`.
47
+ if (t.toLowerCase() === 'now')
48
+ return Math.floor(Date.now() / 1000) - 1;
49
+ const secs = Number(BigInt(encodeScalarParam('Timestamp', t, []).value));
50
+ if (!Number.isSafeInteger(secs) || secs < 0)
51
+ throw new Error(`bad time "${raw}"`);
52
+ if (secs > 0xffffffffffff)
53
+ throw new Error(`"${raw}" exceeds the uint48 lock field`);
54
+ return secs;
55
+ }
26
56
  /** min ≤ max in the type's own ordering (mirrors AbxParamsLib's on-chain check, but earlier + clearer). */
27
57
  function orderedBounds(typeName, min, max) {
28
58
  if (typeName === 'Int256Range')
@@ -31,22 +61,54 @@ function orderedBounds(typeName, min, max) {
31
61
  }
32
62
  export function parseSchemaSpec(spec) {
33
63
  const parts = spec.split(':');
34
- if (parts.length !== 3) {
35
- throw new Error(`--schema "${spec}" — expected key:Type:Auth (a Select adds options: key:Select[A|B|C]:Auth; a Range adds bounds: key:Uint256Range[0..100]:Auth).`);
64
+ if (parts.length !== 3 && parts.length !== 4) {
65
+ throw new Error(`--schema "${spec}" — expected key:Type:Auth (a Select adds options: key:Select[A|B|C]:Auth; a Range adds bounds: key:Uint256Range[0..100]:Auth; add :lock=<when> to freeze the value after a date).`);
66
+ }
67
+ const [key, typeToken, authToken, lockToken] = parts;
68
+ // A 4th field is ONLY ever `lock=…`. Anything else there means a colon leaked out of a label or
69
+ // bound and split the spec wrong — report that as the shape error it is, rather than letting the
70
+ // mangled fragments fail later with a confusing "malformed type".
71
+ if (lockToken !== undefined && !/^lock=/.test(lockToken)) {
72
+ throw new Error(`--schema "${spec}" — expected key:Type:Auth (with an optional 4th field, lock=<when>). ` +
73
+ `Got ${parts.length} colon-separated fields: a ':' inside a Select option or a bound splits the spec — use simpler labels.`);
36
74
  }
37
- const [key, typeToken, authName] = parts;
38
75
  const m = typeToken.match(/^([A-Za-z0-9]+)(?:\[(.*)\])?$/);
39
76
  if (!key || !m)
40
77
  throw new Error(`--schema "${spec}" — malformed type "${typeToken}".`);
41
78
  const typeName = m[1];
42
79
  const bracket = m[2]; // undefined when there is no [...]
43
80
  const paramType = PARAM_TYPES.indexOf(typeName);
81
+ // An Address-bearing leg names its holder inline: `Address(0x…)`, `ArtistOrAddress(0x…)`. The
82
+ // contract pairs auth and authAddress strictly (one without the other reverts InvalidParamSchema),
83
+ // so they belong in one token rather than a second flag that could be forgotten.
84
+ const am = authToken.match(/^([A-Za-z]+)(?:\((0x[0-9a-fA-F]{40})\))?$/);
85
+ if (!am)
86
+ throw new Error(`--schema "${spec}" — malformed auth "${authToken}" (an Address leg looks like Address(0x…)).`);
87
+ const authName = am[1];
44
88
  const auth = AUTH_OPTIONS.indexOf(authName);
45
89
  if (paramType < 0 || auth < 0) {
46
90
  throw new Error(`--schema "${spec}" — Type ∈ {${PARAM_TYPES.join('|')}}, Auth ∈ {${AUTH_OPTIONS.join('|')}}.`);
47
91
  }
48
- if (AUTH_OPTIONS[auth].includes('Address')) {
49
- throw new Error(`--schema "${spec}" — Address-auth legs need an authAddress; set that schema post-deploy via the contract.`);
92
+ const wantsAddress = AUTH_OPTIONS[auth].includes('Address');
93
+ const authAddress = (am[2] ?? ZERO_ADDR);
94
+ if (wantsAddress && authAddress === ZERO_ADDR) {
95
+ throw new Error(`--schema "${spec}" — ${authName} names a specific writer, so it needs one: ${key}:${typeToken}:${authName}(0xYourAddress). ` +
96
+ `A CONTRACT may hold this leg — that is how open/multi-party participation is built (a controller applies its own rules and forwards the write).`);
97
+ }
98
+ if (!wantsAddress && am[2]) {
99
+ throw new Error(`--schema "${spec}" — ${authName} takes no address; only an Address-bearing leg does.`);
100
+ }
101
+ let lockAfter = 0;
102
+ if (lockToken !== undefined) {
103
+ const lm = lockToken.match(/^lock=(.*)$/);
104
+ if (!lm)
105
+ throw new Error(`--schema "${spec}" — the 4th field must be lock=<when> (unix seconds, an ISO date, or "now").`);
106
+ try {
107
+ lockAfter = parseWhen(lm[1]);
108
+ }
109
+ catch (e) {
110
+ throw new Error(`--schema "${spec}" — ${e.message}`);
111
+ }
50
112
  }
51
113
  let min = ZERO32;
52
114
  let max = ZERO32;
@@ -85,7 +147,7 @@ export function parseSchemaSpec(spec) {
85
147
  throw new Error(`--schema "${spec}" — min must be ≤ max (got ${mm[0].trim()}..${mm[1].trim()}).`);
86
148
  }
87
149
  }
88
- return { key, paramType, auth, min, max, selectOptions };
150
+ return { key, paramType, auth, authAddress, lockAfter, min, max, selectOptions };
89
151
  }
90
152
  /** Parse a full `--schema` value: comma-separated specs. */
91
153
  export function parseSchemaSpecs(raw) {
@@ -103,23 +165,12 @@ export function describeSchema(s) {
103
165
  if (type === 'Select')
104
166
  detail = `[${s.selectOptions.join(' | ')}]`;
105
167
  else if (s.min !== ZERO32 || s.max !== ZERO32) {
106
- const lo = decodeBound(type, s.min);
107
- const hi = decodeBound(type, s.max);
168
+ const lo = decodeScalarParam(type, s.min);
169
+ const hi = decodeScalarParam(type, s.max);
108
170
  detail = `[${lo}..${hi}]`;
109
171
  }
110
- return `${s.key}:${type}${detail}:${auth}`;
111
- }
112
- function decodeBound(typeName, v) {
113
- // Cheap display-only decode (the SDK's decodeScalarParam is the canonical one, but this keeps the
114
- // readout dependency-light for the two Range cases that matter in a bound).
115
- if (typeName === 'Int256Range')
116
- return BigInt.asIntN(256, BigInt(v)).toString();
117
- if (typeName === 'DecimalRange') {
118
- const scaled = BigInt(v);
119
- const whole = scaled / 10000000000n;
120
- const frac = (scaled % 10000000000n).toString().padStart(10, '0').replace(/0+$/, '');
121
- return frac ? `${whole}.${frac}` : `${whole}`;
122
- }
123
- return BigInt(v).toString();
172
+ const who = s.authAddress && s.authAddress !== ZERO_ADDR ? `(${s.authAddress})` : '';
173
+ const lock = s.lockAfter ? `:lock=${new Date(s.lockAfter * 1000).toISOString().slice(0, 19)}Z` : '';
174
+ return `${s.key}:${type}${detail}:${auth}${who}${lock}`;
124
175
  }
125
176
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ export interface ServedTokenUri {
2
+ /** The URL followed, verbatim from the contract. `null` when the tokenURI isn't fetchable. */
3
+ url: string | null;
4
+ /** Why there was nothing to fetch, when there was nothing to fetch. */
5
+ skipped: string | null;
6
+ status: number | null;
7
+ contentType: string | null;
8
+ /** The response body as text, untruncated. `null` when the request never completed. */
9
+ body: string | null;
10
+ /** Transport-level failure (DNS, refused, timeout) — distinct from a served error status. */
11
+ error?: string;
12
+ }
13
+ export interface FetchServedOptions {
14
+ timeoutMs?: number;
15
+ fetchFn?: typeof fetch;
16
+ }
17
+ /**
18
+ * Follow a `tokenURI` and return what came back.
19
+ *
20
+ * A `data:` URI is **not** an error here — it IS the document, and a fully-on-chain project's whole
21
+ * point is that there is no server to ask. Saying so plainly is the honest answer; reporting it as a
22
+ * failure would punish the strongest configuration the protocol offers.
23
+ */
24
+ export declare function fetchServedTokenUri(uri: string, opts?: FetchServedOptions): Promise<ServedTokenUri>;
25
+ /** Whether the served answer is one a marketplace could actually use. */
26
+ export declare function servedOk(served: ServedTokenUri): boolean;
27
+ /** Pretty-print a served body when it is JSON, else return it unchanged. Never throws. */
28
+ export declare function prettyBody(body: string | null): string;
29
+ export declare function decodeOnChainJson(uri: string, verbatim?: boolean): string | null;
30
+ //# sourceMappingURL=served.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"served.d.ts","sourceRoot":"","sources":["../src/served.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,cAAc;IAC7B,8FAA8F;IAC9F,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,uEAAuE;IACvE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uFAAuF;IACvF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,6FAA6F;IAC7F,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAID;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,cAAc,CAAC,CAsC7G;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAExD;AAED,0FAA0F;AAC1F,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAOtD;AAKD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,MAAM,GAAG,IAAI,CAqB9E"}
package/dist/served.js ADDED
@@ -0,0 +1,112 @@
1
+ /**
2
+ * What a URL-valued `tokenURI` **actually returns** — the served answer, not the pointer.
3
+ *
4
+ * Backlog B14. `abx tokenuri` read the chain, `abx verify` re-hashed bytes, `abx status` reported the
5
+ * indexing lifecycle — each a different slice, none of them the served JSON body. An agent in a cold
6
+ * sweep fell back to raw `curl` for exactly this, which is the tell that a command was missing.
7
+ *
8
+ * It also answers the question a *warning* couldn't. "Nothing tells you the provider you registered
9
+ * with isn't the base baked on-chain" is real, but the obvious check — compare the remote's base URL
10
+ * to the on-chain one — false-positives on the common custom-domain case (a baked `meta.artist.xyz`
11
+ * fronting `api.provider.xyz`), and a warning that cries wolf on the correct setup is worse than none.
12
+ * Fetching what the **baked** base serves makes the mismatch self-evident instead: you see the other
13
+ * provider's 404, or another project's document.
14
+ *
15
+ * Lives in its own module because `main.ts` exports nothing (it runs `main()` unconditionally), so
16
+ * anything that needs a test seam has to be extracted — the same reason `scaffold.ts` exists.
17
+ */
18
+ import { parseDataUri } from '@artblocks/abx-sdk';
19
+ const DEFAULT_TIMEOUT_MS = 10_000;
20
+ /**
21
+ * Follow a `tokenURI` and return what came back.
22
+ *
23
+ * A `data:` URI is **not** an error here — it IS the document, and a fully-on-chain project's whole
24
+ * point is that there is no server to ask. Saying so plainly is the honest answer; reporting it as a
25
+ * failure would punish the strongest configuration the protocol offers.
26
+ */
27
+ export async function fetchServedTokenUri(uri, opts = {}) {
28
+ if (!/^https?:\/\//i.test(uri)) {
29
+ return {
30
+ url: null,
31
+ skipped: uri.startsWith('data:')
32
+ ? 'the tokenURI IS the document (a data: URI) — it resolves on-chain, so there is no server to ask'
33
+ : `the tokenURI is not an http(s) URL (${uri.slice(0, 40)}…) — nothing to fetch`,
34
+ status: null,
35
+ contentType: null,
36
+ body: null,
37
+ };
38
+ }
39
+ const doFetch = opts.fetchFn ?? fetch;
40
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
41
+ const ctrl = new AbortController();
42
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
43
+ try {
44
+ const res = await doFetch(uri, { signal: ctrl.signal, redirect: 'follow' });
45
+ return {
46
+ url: uri,
47
+ skipped: null,
48
+ status: res.status,
49
+ contentType: res.headers.get('content-type'),
50
+ body: await res.text(),
51
+ };
52
+ }
53
+ catch (err) {
54
+ const aborted = err.name === 'AbortError' || ctrl.signal.aborted;
55
+ return {
56
+ url: uri,
57
+ skipped: null,
58
+ status: null,
59
+ contentType: null,
60
+ body: null,
61
+ error: aborted ? `no response in ${timeoutMs}ms` : err.message,
62
+ };
63
+ }
64
+ finally {
65
+ clearTimeout(timer);
66
+ }
67
+ }
68
+ /** Whether the served answer is one a marketplace could actually use. */
69
+ export function servedOk(served) {
70
+ return served.status !== null && served.status >= 200 && served.status < 300;
71
+ }
72
+ /** Pretty-print a served body when it is JSON, else return it unchanged. Never throws. */
73
+ export function prettyBody(body) {
74
+ if (!body)
75
+ return '';
76
+ try {
77
+ return JSON.stringify(JSON.parse(body), null, 2);
78
+ }
79
+ catch {
80
+ return body; // not JSON — a provider's HTML error page is still worth seeing verbatim
81
+ }
82
+ }
83
+ // Read tokenURI(id) STRAIGHT FROM THE CONTRACT on-chain (no server, no node) and decode
84
+ // the data: URI. For a fully on-chain token (`--onchain-uri`), this is the proof it
85
+ // self-resolves: the renderer assembles the JSON on-chain; any RPC returns it.
86
+ export function decodeOnChainJson(uri, verbatim = false) {
87
+ const parsed = parseDataUri(uri);
88
+ if (!parsed || parsed.mime !== 'application/json')
89
+ return null;
90
+ const raw = parsed.base64 ? Buffer.from(parsed.body, 'base64').toString('utf8') : decodeURIComponent(parsed.body);
91
+ try {
92
+ const obj = JSON.parse(raw);
93
+ // `verbatim` (--json) returns the document EXACTLY as the chain assembled it. Everything below is
94
+ // a courtesy for human eyes, and abbreviating for eyes is fine — abbreviating for a PROGRAM is
95
+ // not. An integrator scraped this output, got a `data:` URI cut to 96 chars that still looked
96
+ // valid, and stored it; the only way to read their own token was to reimplement `eth_call`.
97
+ if (verbatim)
98
+ return JSON.stringify(obj, null, 2);
99
+ for (const k of Object.keys(obj)) {
100
+ const v = obj[k];
101
+ // Truncate only huge inline `data:` blobs (base64 SVGs/images); show plain locator URLs
102
+ // (http/ipfs/ar) IN FULL so the creator can verify where the image actually resolves.
103
+ if (typeof v === 'string' && v.startsWith('data:') && v.length > 96)
104
+ obj[k] = `${v.slice(0, 96)}… (${v.length} chars) [--json for the full value]`;
105
+ }
106
+ return JSON.stringify(obj, null, 2);
107
+ }
108
+ catch {
109
+ return verbatim ? raw : raw.slice(0, 600);
110
+ }
111
+ }
112
+ //# sourceMappingURL=served.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"served.js","sourceRoot":"","sources":["../src/served.ts"],"names":[],"mappings":"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"}
package/dist/signer.d.ts CHANGED
@@ -41,6 +41,19 @@ export interface SignResult {
41
41
  * caller hands the printed tx to an external signer).
42
42
  */
43
43
  export declare function signTx(provider: TxProvider, opts: SignOptions): Promise<SignResult | null>;
44
+ /**
45
+ * Sign + broadcast a SEQUENCE of prepared txs on the hot lane, in order — the hot-lane sibling of
46
+ * the wallet lane's `openWalletSession({total: N})` (one session, N approvals) and the cold lane's
47
+ * per-tx `signTx` loop (see e.g. `deploy-code`'s cold-lane branch in main.ts). One
48
+ * {@link makeHotSender} for the whole sequence, so the nonce is pinned once and gas is re-checked
49
+ * per tx against the target a PRIOR tx in this same sequence may just have created (a multi-tx code
50
+ * deploy: the setup multicall targets the clone the deploy tx just made). Replaces what used to be
51
+ * a hand-rolled nonce/gas loop at the one call site that needed it.
52
+ */
53
+ export declare function signHotSequence(txs: PreparedTx[], opts: {
54
+ chainKey: string;
55
+ yes?: boolean;
56
+ }): Promise<SignResult[]>;
44
57
  /**
45
58
  * The one sign-page implementation behind the whole wallet lane — single owner ops AND
46
59
  * multi-tx staged deploys both run through it ({@link signWallet} just opens one with
@@ -1 +1 @@
1
- {"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,OAAO,EACZ,KAAK,GAAG,EACR,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,MAAM,CAAC;AAE7C;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAEhD,uGAAuG;AACvG,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;AAE9F,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,sGAAsG;IACtG,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8EAA8E;IAC9E,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;gGAC4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,UAAU,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAqED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAShG;AAyFD;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,GAAG,CAAC;IACZ,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,8FAA8F;IAC9F,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5B,6FAA6F;IAC7F,IAAI,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7C;oGACgG;IAChG,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjE,yEAAyE;IACzE,KAAK,IAAI,IAAI,CAAC;CACf;AASD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,2FAA2F;IAC3F,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CA8I1F"}
1
+ {"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"AAGA,OAAO,EASL,KAAK,OAAO,EACZ,KAAK,GAAG,EACR,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,MAAM,CAAC;AAG7C;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAEhD,uGAAuG;AACvG,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;AAE9F,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,sGAAsG;IACtG,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8EAA8E;IAC9E,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;gGAC4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,UAAU,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAmED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAShG;AA+BD;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAC,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAqBvH;AA4ED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,GAAG,CAAC;IACZ,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,8FAA8F;IAC9F,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5B,6FAA6F;IAC7F,IAAI,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7C;oGACgG;IAChG,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjE,yEAAyE;IACzE,KAAK,IAAI,IAAI,CAAC;CACf;AASD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,2FAA2F;IAC3F,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CA8J1F"}
package/dist/signer.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createServer } from 'node:http';
2
2
  import { writeFileSync } from 'node:fs';
3
- import { makePublicClient, makeWalletClient, resolveChain, } from '@artblocks/abx-sdk';
3
+ import { makeHotSender, makePublicClient, makeWalletClient, pinGas, resolveChain, runPrepared, sleep, waitForCodeAt, } from '@artblocks/abx-sdk';
4
+ import { faucetHint } from './config.js';
4
5
  /** Announce the wallet sign-page URL: a stable, greppable line for humans/agents, plus an
5
6
  * optional file write so a backgrounding agent can read the URL without parsing stdout. */
6
7
  function announceSignUrl(signUrl, file) {
@@ -31,7 +32,6 @@ function explorerFor(chainKey) {
31
32
  async function resolveTx(provider, signer) {
32
33
  return typeof provider === 'function' ? await provider(signer) : provider;
33
34
  }
34
- const delay = (ms) => new Promise((r) => setTimeout(r, ms));
35
35
  /**
36
36
  * Bind the sign page to a *fresh* port, robustly.
37
37
  *
@@ -64,7 +64,7 @@ async function listenAvailable(server, preferred) {
64
64
  for (let i = 0; i < 6; i++) {
65
65
  if (await tryListen(preferred))
66
66
  return preferred;
67
- await delay(300);
67
+ await sleep(300);
68
68
  }
69
69
  // Still held (a stuck/abandoned session) → don't block or crash; take a fresh ephemeral port.
70
70
  if (await tryListen(0))
@@ -107,17 +107,46 @@ async function signHot(provider, opts) {
107
107
  console.log(`\n ${C.orange}⚠${C.reset} env key ${account.address} is not the expected signer ${opts.expectedSigner} — this will likely revert.`);
108
108
  }
109
109
  console.log(`\n ${dim(`signing with env key ${account.address} …`)}`);
110
- const txHash = await wallet.sendTransaction({
110
+ // makeHotSender pins the gas (rather than letting viem re-estimate at send time — see
111
+ // execute.ts's pinGas for why an estimate can come back as the calldata cost alone and silently
112
+ // underfund a CREATE) and throws a typed error on a `reverted` receipt instead of reporting a
113
+ // burned transaction as "confirmed".
114
+ const send = makeHotSender({ wallet, account, publicClient });
115
+ const receipt = await send(prepared);
116
+ console.log(` ${dim('tx')} ${explorerFor(opts.chainKey)}/tx/${receipt.transactionHash}`);
117
+ console.log(` ${green('✓')} confirmed`);
118
+ return { txHash: receipt.transactionHash, prepared, blockNumber: receipt.blockNumber };
119
+ }
120
+ /**
121
+ * Sign + broadcast a SEQUENCE of prepared txs on the hot lane, in order — the hot-lane sibling of
122
+ * the wallet lane's `openWalletSession({total: N})` (one session, N approvals) and the cold lane's
123
+ * per-tx `signTx` loop (see e.g. `deploy-code`'s cold-lane branch in main.ts). One
124
+ * {@link makeHotSender} for the whole sequence, so the nonce is pinned once and gas is re-checked
125
+ * per tx against the target a PRIOR tx in this same sequence may just have created (a multi-tx code
126
+ * deploy: the setup multicall targets the clone the deploy tx just made). Replaces what used to be
127
+ * a hand-rolled nonce/gas loop at the one call site that needed it.
128
+ */
129
+ export async function signHotSequence(txs, opts) {
130
+ if (!TESTNETS.has(opts.chainKey) && !opts.yes) {
131
+ throw new Error(`Refusing to sign on '${opts.chainKey}' with the env key without --yes. ` +
132
+ `For real value, prefer the wallet lane (--sign) so the key never touches this process.`);
133
+ }
134
+ const { wallet, account } = makeWalletClient({ chainKey: opts.chainKey });
135
+ const publicClient = makePublicClient({ chainKey: opts.chainKey });
136
+ console.log(`\n ${dim(`signing ${txs.length} transaction(s) with env key ${account.address} …`)}`);
137
+ const send = makeHotSender({
138
+ wallet,
111
139
  account,
112
- chain: wallet.chain,
113
- to: prepared.to ?? undefined,
114
- data: prepared.data,
115
- value: BigInt(prepared.value),
140
+ publicClient,
141
+ onEvent: (e) => {
142
+ if (e.kind === 'sending')
143
+ console.log(` ${purple('◆')} ${bold(e.tx.summary)}`);
144
+ else
145
+ console.log(` ${dim('tx')} ${explorerFor(opts.chainKey)}/tx/${e.receipt.transactionHash} ${green('✓')} confirmed`);
146
+ },
116
147
  });
117
- console.log(` ${dim('tx')} ${explorerFor(opts.chainKey)}/tx/${txHash}`);
118
- const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
119
- console.log(` ${green('✓')} confirmed`);
120
- return { txHash, prepared, blockNumber: receipt.blockNumber };
148
+ const receipts = await runPrepared(txs, send);
149
+ return receipts.map((receipt, i) => ({ txHash: receipt.transactionHash, prepared: txs[i], blockNumber: receipt.blockNumber }));
121
150
  }
122
151
  // ── cold lane: print the tx for an external signer ───────────────────────────
123
152
  async function signCold(provider, opts) {
@@ -127,10 +156,26 @@ async function signCold(provider, opts) {
127
156
  const prepared = await resolveTx(provider, opts.expectedSigner ?? '0x0000000000000000000000000000000000000000');
128
157
  printIntent(prepared, opts);
129
158
  console.log(`\n ${bold('unsigned transaction')} ${dim('— sign + broadcast this with your own signer (multisig / offline):')}\n`);
130
- console.log(JSON.stringify({ to: prepared.to, data: prepared.data, value: prepared.value, chainId: prepared.chainId }, null, 2)
159
+ console.log(JSON.stringify({
160
+ to: prepared.to,
161
+ data: prepared.data,
162
+ value: prepared.value,
163
+ chainId: prepared.chainId,
164
+ // NOT a gas limit — a floor the external signer's own estimate must clear. Emitting it as
165
+ // `gas` would be wrong: it covers only the provable code-deposit cost, not the schema /
166
+ // dependency / mint legs riding the same multicall, so a signer that used it verbatim would
167
+ // under-fund the transaction. See execute.ts.
168
+ ...(prepared.gasFloor ? { gasMustExceed: prepared.gasFloor } : {}),
169
+ }, null, 2)
131
170
  .split('\n')
132
171
  .map((l) => ' ' + l)
133
172
  .join('\n'));
173
+ if (prepared.gasFloor) {
174
+ console.log(`\n ${C.orange}⚠${C.reset} ${bold('gasMustExceed')} ${dim('is a floor, not a limit.')} This tx stores bytes on-chain, which costs\n` +
175
+ ` 200 gas/byte in code deposit alone. If your signer's gas estimate comes back BELOW ${BigInt(prepared.gasFloor)},\n` +
176
+ ` it is estimating against stale state (the target contract not visible yet) — re-estimate rather than\n` +
177
+ ` sending, or it reverts ${bold('DeploymentFailed()')} and the gas is lost.`);
178
+ }
134
179
  console.log(`\n ${dim('Then run')} ${bold('abx index <address>')} ${dim('to reflect it once mined.')}\n`);
135
180
  return null;
136
181
  }
@@ -189,6 +234,8 @@ export async function openWalletSession(opts) {
189
234
  // txHash) or a message to sign (personal_sign → signature) — the latter is how the wallet signs
190
235
  // Turbo (Arweave) upload data-items so its own credits pay. Both resolve `pendingResolve` with a
191
236
  // Hex value (a txHash or a signature); only one item is ever in flight.
237
+ // `tx` carries an optional resolved `gas` (hex) the CLI pinned for this item — the page forwards it
238
+ // to `eth_sendTransaction` rather than letting the wallet estimate. See send() below.
192
239
  let current = null;
193
240
  let pendingResolve = null;
194
241
  let pendingReject = null;
@@ -270,6 +317,12 @@ export async function openWalletSession(opts) {
270
317
  const signUrl = `http://localhost:${boundPort}`;
271
318
  console.log(`\n ${purple('◆')} ${bold('wallet signing')} ${dim(`(${chain.name}${opts.total ? `, ${opts.total} transactions` : ''})`)}`);
272
319
  console.log(`\n Open ${bold(signUrl)} , connect a wallet${opts.expectedSigner ? ` (${opts.expectedSigner})` : ''}, and approve each step.`);
320
+ // Funding, said out loud at the one moment it matters. `warnUnfunded` can't help here: on the
321
+ // wallet lane without --for we don't know the address until the browser connects, so an empty
322
+ // wallet's first and only signal used to be a failed transaction. Name the requirement and the
323
+ // fix BEFORE they go pick a wallet, not after it reverts.
324
+ console.log(` ${dim(`Use a wallet holding a little ${chain.name} ETH — it pays gas, and it's free test ETH, not real money.`)}`);
325
+ console.log(` ${dim(`None yet? ${faucetHint(opts.chainKey)}.`)}`);
273
326
  announceSignUrl(signUrl, opts.signUrlFile);
274
327
  console.log(` ${dim('The key never touches this process — only signed tx hashes come back. Ctrl-C to cancel.')}\n`);
275
328
  const onSigint = () => fail(new Error('cancelled'));
@@ -278,7 +331,16 @@ export async function openWalletSession(opts) {
278
331
  connect: () => connected,
279
332
  async send(tx) {
280
333
  index += 1;
281
- current = { index, kind: 'tx', tx, consumed: false };
334
+ // A wallet session spans blocks: the deploy is approved, then setup targets the contract it
335
+ // just created. The browser wallet estimates against ITS OWN RPC, which we don't control and
336
+ // which can lag — so wait for the code to be visible and hand the page an explicit gas limit
337
+ // instead of letting the wallet guess. Same reasoning as the hot lane (see execute.ts).
338
+ if (tx.to && tx.gasFloor)
339
+ await waitForCodeAt(publicClient, tx.to);
340
+ const gas = opts.expectedSigner
341
+ ? await pinGas(publicClient, { from: opts.expectedSigner, to: tx.to, data: tx.data, value: tx.value, gasFloor: tx.gasFloor }).catch(() => undefined)
342
+ : undefined;
343
+ current = { index, kind: 'tx', tx: gas === undefined ? tx : { ...tx, gas: `0x${gas.toString(16)}` }, consumed: false };
282
344
  console.log(` ${dim(`tx ${opts.total ? `${index}/${opts.total}` : index}:`)} ${tx.summary} ${dim('— approve in your wallet …')}`);
283
345
  const txHash = await new Promise((resolve, reject) => {
284
346
  pendingResolve = resolve;
@@ -492,6 +554,10 @@ $('sign').onclick = async () => {
492
554
  } else {
493
555
  const params = {from: account, data: curTx.data, value: curTx.value};
494
556
  if (curTx.to) params.to = curTx.to;
557
+ // An explicit limit the CLI resolved for this tx. Wallets that would otherwise estimate against
558
+ // a node lagging behind the deploy block send ~200k for a call needing ~941k, and the on-chain
559
+ // content write reverts DeploymentFailed(). The user can still edit it in the wallet UI.
560
+ if (curTx.gas) params.gas = curTx.gas;
495
561
  const txHash = await window.ethereum.request({method:'eth_sendTransaction', params:[params]});
496
562
  await fetch('/signed', {method:'POST', headers:{'content-type':'application/json'}, body: JSON.stringify({index: curIndex, txHash})});
497
563
  const link = CFG.explorer ? '<a href="'+CFG.explorer+'/tx/'+txHash+'" target="_blank">'+txHash+'</a>' : txHash;