quantumcoin 7.0.1 → 7.0.3

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 (43) hide show
  1. package/.gitignore +3 -0
  2. package/README-SDK.md +64 -10
  3. package/README.md +27 -4
  4. package/SPEC.md +3843 -0
  5. package/examples/AllSolidityTypes.sol +184 -0
  6. package/examples/SimpleIERC20.sol +74 -0
  7. package/examples/example-generator-sdk-js.js +95 -0
  8. package/examples/example-generator-sdk-ts.js +95 -0
  9. package/examples/example.js +2 -2
  10. package/examples/offline-signing.js +73 -0
  11. package/examples/package-lock.json +10 -1103
  12. package/examples/package.json +1 -2
  13. package/examples/read-operations.js +1 -2
  14. package/examples/sdk-generator-erc20.inline.json +251 -0
  15. package/examples/solidity-types.ts +43 -0
  16. package/generate-sdk.js +689 -87
  17. package/package.json +30 -9
  18. package/src/abi/interface.d.ts +18 -0
  19. package/src/abi/interface.js +247 -9
  20. package/src/abi/js-abi-coder.js +474 -0
  21. package/src/contract/contract-factory.d.ts +1 -1
  22. package/src/contract/contract-factory.js +14 -2
  23. package/src/contract/contract.d.ts +10 -1
  24. package/src/contract/contract.js +42 -0
  25. package/src/generator/index.js +1041 -63
  26. package/src/index.d.ts +16 -0
  27. package/src/providers/provider.d.ts +20 -11
  28. package/src/providers/provider.js +12 -0
  29. package/src/types/index.d.ts +462 -0
  30. package/src/types/index.js +9 -0
  31. package/test/e2e/all-solidity-types.dynamic.test.js +200 -0
  32. package/test/e2e/all-solidity-types.fixtures.js +231 -0
  33. package/test/e2e/all-solidity-types.generated-sdks.e2e.test.js +368 -0
  34. package/test/e2e/simple-erc20.generated-sdks.e2e.test.js +151 -0
  35. package/test/e2e/transactional.test.js +4 -4
  36. package/test/e2e/typed-generator.e2e.test.js +8 -6
  37. package/test/integration/ws-provider.test.js +1 -1
  38. package/test/unit/generate-contract-cli.test.js +2 -1
  39. package/test/unit/generate-sdk-artifacts-json.test.js +45 -0
  40. package/test/unit/generator.test.js +1 -0
  41. package/test/unit/populate-transaction.test.js +62 -0
  42. package/test/unit/solidity-types.test.js +46 -0
  43. package/test/unit/utils.test.js +1 -1
package/generate-sdk.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * quantumcoin-sdk-generator
3
+ * sdkgen
4
4
  *
5
5
  * NOTE: This script is the SDK generator CLI entrypoint.
6
6
  *
@@ -20,13 +20,13 @@ const readline = require("node:readline/promises");
20
20
  const { stdin, stdout } = require("node:process");
21
21
  const { execFileSync, spawnSync } = require("node:child_process");
22
22
 
23
- const { generate, generateFromArtifacts, generateTransactionalTestJs } = require("./src/generator");
23
+ const { generate, generateFromArtifacts, generateTransactionalTestJs, generateAllContractsTransactionalTestJs } = require("./src/generator");
24
24
 
25
25
  function _helpText() {
26
26
  return `
27
- quantumcoin-sdk-generator (generate-sdk.js)
27
+ sdkgen (generate-sdk.js)
28
28
 
29
- Generates TypeScript contract wrappers (plus optional package scaffold, examples and transactional tests).
29
+ Generates TypeScript or JavaScript contract wrappers (plus optional package scaffold, examples and transactional tests).
30
30
 
31
31
  If you run this script with no parameters, it prints this help.
32
32
 
@@ -43,12 +43,35 @@ INPUT MODES
43
43
  --sol "<A.sol,B.sol,...>" (comma-separated list; can be 1+ files)
44
44
  --name <ContractName> (optional; if omitted, generates ALL deployable contracts found)
45
45
  --solc <path/to/solc.exe> (optional; defaults from env; see ENV below)
46
+ --solc-args "<args>" (optional; extra args passed to solc, e.g. "--via-ir --evm-version london")
46
47
 
47
48
  3) Artifacts JSON (array of ABI+BIN pairs)
48
49
  --artifacts-json <path/to/artifacts.json>
49
50
  The JSON file should be an array. Each entry supports:
50
- - { abi: "<path|abiArray>", bin: "<path|0x...>", name?: "<ContractName>" }
51
+ - { abi: "<path|abiJsonString|abiArray>", bin: "<path|0x...>", name?: "<ContractName>" }
51
52
  If "name" is omitted and abi is a path, the contract name defaults from the ABI filename.
53
+
54
+ Example artifacts.json (ABI+BIN pairs):
55
+ [
56
+ {
57
+ "abi": "./artifacts/Alpha.abi.json",
58
+ "bin": "./artifacts/Alpha.bin",
59
+ "name": "Alpha"
60
+ },
61
+ {
62
+ "abi": [
63
+ {
64
+ "type": "function",
65
+ "name": "set",
66
+ "stateMutability": "nonpayable",
67
+ "inputs": [{ "name": "value", "type": "uint256" }],
68
+ "outputs": []
69
+ }
70
+ ],
71
+ "bin": "0x6000600055...",
72
+ "name": "Beta"
73
+ }
74
+ ]
52
75
 
53
76
  PACKAGE OUTPUT (optional)
54
77
  --create-package
@@ -61,19 +84,22 @@ PACKAGE OUTPUT (optional)
61
84
 
62
85
  Notes:
63
86
  - When --create-package is used, the generator creates a complete npm package scaffold with:
64
- - src/*.ts (typed contracts + factories)
87
+ - src/*.(ts|js) (contracts + factories; depends on --lang)
65
88
  - test/e2e/*.e2e.test.js (transactional tests)
66
89
  - examples/ (deploy/read/write/events)
67
90
  - artifacts/ (only when using --sol)
68
- - index.js + index.d.ts + README.md (generated by build scripts)
69
- - The generator will run:
91
+ - index.js + index.d.ts (package entry shims; TS mode uses dist/)
92
+ - For --lang ts, the generator will run:
70
93
  npm install
71
94
  npm run build:ts
72
- npm run build-powershell (Windows) or npm run build (non-Windows)
73
- as the final step, so README.md and index.d.ts are produced immediately.
95
+ as the final step.
96
+ - For --lang js, the generator will run:
97
+ npm install
98
+ and no build step is required.
74
99
 
75
100
  GENERAL OPTIONS
76
101
  --out <folder> Output folder (default: ./generated-contract)
102
+ --lang <ts|js> Output language for generated contract sources (default: ts)
77
103
  --non-interactive | --yes Disable prompts (required for CI)
78
104
  -h | --help Show this help
79
105
 
@@ -112,6 +138,14 @@ function _hasFlag(argv, name) {
112
138
  return argv.includes(name);
113
139
  }
114
140
 
141
+ function _normalizeLang(v) {
142
+ const t = (v == null ? "ts" : String(v)).trim().toLowerCase();
143
+ if (!t) return "ts";
144
+ if (t === "ts" || t === "typescript") return "ts";
145
+ if (t === "js" || t === "javascript") return "js";
146
+ throw new Error(`Invalid --lang: ${v} (expected "ts" or "js")`);
147
+ }
148
+
115
149
  function _basenameNoExt(p) {
116
150
  const b = path.basename(p);
117
151
  const i = b.lastIndexOf(".");
@@ -168,13 +202,26 @@ function _readArtifactsJson(fileAbs) {
168
202
  let abi;
169
203
  let abiPath = null;
170
204
  if (typeof abiField === "string") {
171
- abiPath = path.resolve(baseDir, abiField);
172
- if (!fs.existsSync(abiPath)) throw new Error(`ABI file not found (entry ${i}): ${abiPath}`);
173
- abi = _readJson(abiPath);
205
+ // Support either:
206
+ // - file path to an ABI JSON file
207
+ // - inline ABI JSON string (encoded ABI array)
208
+ const maybePath = path.resolve(baseDir, abiField);
209
+ if (fs.existsSync(maybePath) && fs.statSync(maybePath).isFile()) {
210
+ abiPath = maybePath;
211
+ abi = _readJson(abiPath);
212
+ } else {
213
+ try {
214
+ abi = JSON.parse(abiField);
215
+ } catch (e) {
216
+ throw new Error(
217
+ `Invalid "abi" for artifacts entry ${i} (expected ABI file path or ABI JSON string). Not found: ${maybePath}`,
218
+ );
219
+ }
220
+ }
174
221
  } else if (Array.isArray(abiField)) {
175
222
  abi = abiField;
176
223
  } else {
177
- throw new Error(`Invalid "abi" for artifacts entry ${i} (expected path or ABI array)`);
224
+ throw new Error(`Invalid "abi" for artifacts entry ${i} (expected path, ABI JSON string, or ABI array)`);
178
225
  }
179
226
  if (!Array.isArray(abi)) throw new Error(`ABI must be an array (entry ${i})`);
180
227
 
@@ -243,18 +290,52 @@ function _findConstructor(abi) {
243
290
  return ctor || { type: "constructor", inputs: [] };
244
291
  }
245
292
 
246
- function _solTypeToExampleValueExpr(type) {
247
- if (typeof type !== "string") return "undefined";
293
+ function _solTypeToExampleValueExpr(param) {
294
+ const type = typeof param === "string" ? param : String(param && param.type ? param.type : "");
295
+ const internalType = typeof param === "object" && param ? String(param.internalType || "") : "";
296
+ if (!type) return "undefined";
297
+
298
+ // Arrays (dynamic or fixed)
248
299
  if (type.endsWith("]")) {
249
300
  const inner = type.slice(0, type.lastIndexOf("["));
250
- return `[${_solTypeToExampleValueExpr(inner)}]`;
301
+ const bracket = type.slice(type.lastIndexOf("[") + 1, type.length - 1);
302
+ const isFixed = bracket.length > 0;
303
+ const fixedLen = isFixed ? Number(bracket) : 0;
304
+ const elemParam = { ...(param || {}), type: inner };
305
+ const elemExpr = _solTypeToExampleValueExpr(elemParam);
306
+ if (isFixed && Number.isFinite(fixedLen) && fixedLen > 0) {
307
+ // Fixed arrays MUST match the exact declared length.
308
+ return `Array(${fixedLen}).fill(${elemExpr})`;
309
+ }
310
+ return `[${elemExpr}]`;
251
311
  }
312
+
252
313
  if (type === "address") return "wallet.address";
253
314
  if (type === "bool") return "true";
254
315
  if (type === "string") return JSON.stringify("hello");
255
- if (type === "bytes") return JSON.stringify("0x");
256
- if (type === "bytes32") return `encodeBytes32String("init")`;
257
- if (type.startsWith("uint") || type.startsWith("int")) return "123n";
316
+ if (type === "bytes") return JSON.stringify("0x1234");
317
+
318
+ const mBytesN = type.match(/^bytes(\d+)$/);
319
+ if (mBytesN) {
320
+ const n = Number(mBytesN[1]);
321
+ if (Number.isFinite(n) && n >= 1 && n <= 32) return JSON.stringify(`0x${"11".repeat(n)}`);
322
+ }
323
+
324
+ // Use plain numbers/strings for ints/uints.
325
+ if (type.startsWith("uint") && /\benum\b/.test(internalType)) return "1";
326
+ if (type.startsWith("uint")) return "123";
327
+ if (type.startsWith("int")) return "-123";
328
+
329
+ if (type === "tuple") {
330
+ const comps = Array.isArray(param && param.components) ? param.components : [];
331
+ if (comps.length === 0) return "{}";
332
+ const fields = comps.map((c, idx) => {
333
+ const name = c && typeof c.name === "string" && c.name ? c.name : `field${idx}`;
334
+ return `${JSON.stringify(name)}: ${_solTypeToExampleValueExpr(c)}`;
335
+ });
336
+ return `{ ${fields.join(", ")} }`;
337
+ }
338
+
258
339
  return "undefined";
259
340
  }
260
341
 
@@ -266,6 +347,69 @@ function _parseCommaSeparatedFiles(v) {
266
347
  .filter(Boolean);
267
348
  }
268
349
 
350
+ function _parseSolcExtraArgs(raw) {
351
+ if (!raw) return [];
352
+ const t = String(raw).trim();
353
+ if (!t) return [];
354
+
355
+ // Allow a JSON array string: ["--via-ir","--evm-version","london"]
356
+ if (t.startsWith("[")) {
357
+ const arr = JSON.parse(t);
358
+ if (!Array.isArray(arr) || !arr.every((x) => typeof x === "string")) {
359
+ throw new Error(`--solc-args JSON must be a string[] (got: ${t.slice(0, 80)}${t.length > 80 ? "..." : ""})`);
360
+ }
361
+ return arr.filter((s) => s.trim()).map((s) => s.trim());
362
+ }
363
+
364
+ // Otherwise treat as a single string of space-separated args (basic quoting support).
365
+ /** @type {string[]} */
366
+ const out = [];
367
+ let cur = "";
368
+ let inQuote = null; // "'" | "\""
369
+ let escaping = false;
370
+
371
+ for (let i = 0; i < t.length; i++) {
372
+ const ch = t[i];
373
+
374
+ if (escaping) {
375
+ cur += ch;
376
+ escaping = false;
377
+ continue;
378
+ }
379
+
380
+ if (ch === "\\") {
381
+ // allow escaping inside quotes (and harmless outside)
382
+ escaping = true;
383
+ continue;
384
+ }
385
+
386
+ if (inQuote) {
387
+ if (ch === inQuote) {
388
+ inQuote = null;
389
+ } else {
390
+ cur += ch;
391
+ }
392
+ continue;
393
+ }
394
+
395
+ if (ch === '"' || ch === "'") {
396
+ inQuote = ch;
397
+ continue;
398
+ }
399
+
400
+ if (/\s/.test(ch)) {
401
+ if (cur) out.push(cur);
402
+ cur = "";
403
+ continue;
404
+ }
405
+
406
+ cur += ch;
407
+ }
408
+
409
+ if (cur) out.push(cur);
410
+ return out;
411
+ }
412
+
269
413
  function _resolveSolcPath(argv) {
270
414
  const solcArg = _argValue(argv, "--solc") || _argValue(argv, "--solc-path") || _argValue(argv, "--solcPath");
271
415
  const env =
@@ -275,7 +419,7 @@ function _resolveSolcPath(argv) {
275
419
  process.env.SOLC_PATH ||
276
420
  process.env.solc ||
277
421
  null;
278
- return env || "c:\\gethbuild\\solc.exe";
422
+ return env || "c:\\solc\\solc.exe";
279
423
  }
280
424
 
281
425
  function _assertSolcExists(solcPath) {
@@ -286,9 +430,11 @@ function _assertSolcExists(solcPath) {
286
430
  }
287
431
  }
288
432
 
289
- function _compileSolidityToArtifacts({ solcPath, solFilesAbs, contractNameFilter }) {
433
+ function _compileSolidityToArtifacts({ solcPath, solFilesAbs, contractNameFilter, solcExtraArgs }) {
290
434
  _assertSolcExists(solcPath);
291
- const out = execFileSync(solcPath, ["--optimize", "--combined-json", "abi,bin", ...solFilesAbs], { encoding: "utf8" });
435
+ const extra = Array.isArray(solcExtraArgs) ? solcExtraArgs : [];
436
+ const base = extra.includes("--optimize") ? [] : ["--optimize"];
437
+ const out = execFileSync(solcPath, [...base, ...extra, "--combined-json", "abi,bin", ...solFilesAbs], { encoding: "utf8" });
292
438
  const parsed = JSON.parse(out);
293
439
 
294
440
  const artifacts = [];
@@ -433,16 +579,17 @@ function _extractSolDocs(solFilesAbs) {
433
579
  return out;
434
580
  }
435
581
 
436
- function _renderPackageIndexJs({ artifacts }) {
582
+ function _renderPackageIndexJs({ artifacts, entryDir }) {
583
+ const dir = entryDir || "dist";
437
584
  const lines = [];
438
585
  lines.push("/**");
439
586
  lines.push(" * Auto-generated typed contract package.");
440
587
  lines.push(" *");
441
- lines.push(" * This file is used by `jsdoc-to-markdown` (jsdoc2md) to generate README.md.");
588
+ lines.push(` * This file re-exports the package entry bundle in \`${dir}/\`.`);
442
589
  lines.push(" */");
443
590
  lines.push("");
444
- lines.push("const dist = require(\"./dist\");");
445
- lines.push("Object.assign(exports, dist);");
591
+ lines.push(`const entry = require("./${dir}");`);
592
+ lines.push("Object.assign(exports, entry);");
446
593
  lines.push("");
447
594
 
448
595
  for (const a of artifacts) {
@@ -460,8 +607,8 @@ function _renderPackageIndexJs({ artifacts }) {
460
607
  lines.push(` * ${a.contractName}`);
461
608
  lines.push(" */");
462
609
  }
463
- lines.push(`exports.${a.contractName} = dist.${a.contractName};`);
464
- lines.push(`exports.${a.contractName}__factory = dist.${a.contractName}__factory;`);
610
+ lines.push(`exports.${a.contractName} = entry.${a.contractName};`);
611
+ lines.push(`exports.${a.contractName}__factory = entry.${a.contractName}__factory;`);
465
612
  lines.push("");
466
613
  }
467
614
 
@@ -503,6 +650,7 @@ async function main() {
503
650
  const abiPathArg = _argValue(argv, "--abi");
504
651
  const binPathArg = _argValue(argv, "--bin");
505
652
  const solArg = _argValue(argv, "--sol") || _argValue(argv, "--sol-files") || _argValue(argv, "--solFiles");
653
+ const solcArgsRaw = _argValue(argv, "--solc-args") || _argValue(argv, "--solcArgs");
506
654
  const artifactsJsonArg =
507
655
  _argValue(argv, "--artifacts-json") || _argValue(argv, "--artifactsJson") || _argValue(argv, "--artifacts");
508
656
  const outArg = _argValue(argv, "--out");
@@ -515,6 +663,7 @@ async function main() {
515
663
  const pkgAuthorArg = _argValue(argv, "--package-author") || "";
516
664
  const pkgLicenseArg = _argValue(argv, "--package-license") || "MIT";
517
665
  const pkgVersionArg = _argValue(argv, "--package-version") || "0.0.1";
666
+ const lang = _normalizeLang(_argValue(argv, "--lang") || _argValue(argv, "--language") || _argValue(argv, "--type"));
518
667
 
519
668
  // Decide input type.
520
669
  let inputType = null; // "abibin" | "sol" | "artifactsjson"
@@ -534,6 +683,7 @@ async function main() {
534
683
  let absBin = null;
535
684
  let solFilesAbs = [];
536
685
  let artifactsJsonAbs = null;
686
+ const solcExtraArgs = solcArgsRaw ? _parseSolcExtraArgs(solcArgsRaw) : [];
537
687
 
538
688
  if (!nonInteractive) {
539
689
  const rl0 = readline.createInterface({ input: stdin, output: stdout });
@@ -631,6 +781,7 @@ async function main() {
631
781
  pkgAuthor,
632
782
  pkgLicense,
633
783
  pkgVersion,
784
+ lang,
634
785
  });
635
786
  } else {
636
787
  const target = (await rl.question("Enter the location in your existing package (relative to package root): ")).trim();
@@ -656,6 +807,7 @@ async function main() {
656
807
  pkgAuthor: pkgAuthorArg,
657
808
  pkgLicense: pkgLicenseArg,
658
809
  pkgVersion: pkgVersionArg,
810
+ lang,
659
811
  });
660
812
  }
661
813
 
@@ -674,7 +826,7 @@ async function main() {
674
826
  artifacts = [{ contractName, abi, bytecode, docs: null }];
675
827
  } else if (inputType === "sol") {
676
828
  const solcPath = _resolveSolcPath(argv);
677
- artifacts = _compileSolidityToArtifacts({ solcPath, solFilesAbs, contractNameFilter: contractName });
829
+ artifacts = _compileSolidityToArtifacts({ solcPath, solFilesAbs, contractNameFilter: contractName, solcExtraArgs });
678
830
 
679
831
  // Attach Solidity doc comments (NatSpec) to artifacts
680
832
  const docsByContract = _extractSolDocs(solFilesAbs);
@@ -692,9 +844,9 @@ async function main() {
692
844
  if (artifacts.length === 1) {
693
845
  // Keep the old API/behavior for single-contract generation.
694
846
  const a = artifacts[0];
695
- generateFromArtifacts({ outDir: targetSrcDir, artifacts: [a] });
847
+ generateFromArtifacts({ outDir: targetSrcDir, artifacts: [a], lang });
696
848
  } else {
697
- generateFromArtifacts({ outDir: targetSrcDir, artifacts });
849
+ generateFromArtifacts({ outDir: targetSrcDir, artifacts, lang });
698
850
  }
699
851
 
700
852
  if (createPackage) {
@@ -703,7 +855,9 @@ async function main() {
703
855
  _packageReadme({
704
856
  pkgName: pkgNameArg || path.basename(outDir),
705
857
  pkgDesc: pkgDescArg,
706
- contractNames: artifacts.map((a) => a.contractName),
858
+ artifacts,
859
+ createdFromSolidity: inputType === "sol",
860
+ lang,
707
861
  }),
708
862
  );
709
863
 
@@ -711,6 +865,15 @@ async function main() {
711
865
  _ensureDir(path.join(outDir, "test", "e2e"));
712
866
  _ensureDir(path.join(outDir, "examples"));
713
867
 
868
+ // Shared helper for examples (keeps examples runnable via plain `node`).
869
+ // WARNING: test-only wallet; never use for real funds.
870
+ _writeText(
871
+ path.join(outDir, "examples", "_test-wallet.js"),
872
+ `const { Wallet } = require("quantumcoin");\n\n// Hardcoded test wallet (test-only; never use for real funds)\nconst TEST_WALLET_ENCRYPTED_JSON =\n ${JSON.stringify(
873
+ "{\"address\":\"1a846abe71c8b989e8337c55d608be81c28ab3b2e40c83eaa2a68d516049aec6\",\"crypto\":{\"cipher\":\"aes-256-ctr\",\"ciphertext\":\"ab7e620dd66cb55ac201b9c6796de92bbb06f3681b5932eabe099871f1f7d79acabe30921a39ad13bfe74f42c515734882b6723760142aa3e26e011df514a534ae47bd15d86badd9c6f17c48d4c892711d54d441ee3a0ee0e5b060f816e79c7badd13ff4c235934b1986774223ecf6e8761388969bb239c759b54c8c70e6a2e27c93a4b70129c8159f461d271ae8f3573414c78b88e4d0abfa6365ed45456636d4ed971c7a0c6b84e6f0c2621e819268b135e2bcc169a54d1847b39e6ba2ae8ec969b69f330b7db9e785ed02204d5a1185915ae5338b0f40ef2a7f4d5aaf7563d502135e57f4eb89d5ec1efa5c77e374969d6cd85be625a2ed1225d68ecdd84067bfc69adb83ecd5c6050472eca28a5a646fcdd28077165c629975bec8a79fe1457cb53389b788b25e1f8eff8b2ca326d7dfcaba3f8839225a08057c018a458891fd2caa0d2b27632cffd80f592147ccec9a10dc8a08a48fb55047bff5cf85cda39eb089096bef63842fc3686412f298a54a9e4b0bf4ad36907ba373cbd6d32e7ac494af371da5aa9d38a3463220865114c4adc5e4ac258ba9c6af9fa2ddfd1aec2e16887e4b3977c69561df8599ac9d411c9dd2a4d57f92ea4e5c02aae3f49fb3bc83e16673e6c2dbe96bb181c8dfd0f9757ade2e4ff27215a836058c5ffeab042f6f97c7c02339f76a6284680e01b4bb733690eb3347fbfcc26614b8bf755f9dfce3fea9d4e4d15b164983201732c2e87593a86bca6da6972e128490338f76ae68135888070f4e59e90db54d23834769bdbda9769213faf5357f9167a224523975a946367b68f0cec98658575609f58bfd329e420a921c06713326e4cb20a0df1d77f37e78a320a637a96c604ca3fa89e24beb42313751b8f09b14f9c14c77e4fd13fc6382505d27c771bca0d821ec7c3765acffa99d83c50140a56b0b28101c762bd682fe55cb6f23cbeb3f421d7b36021010e45ac27160dd7ead99c864a1b550c7edb1246950fe32dcc049799f9085287f0a747a6ef7a023df46a23a22f3e833bbf8d404f84344870492658256ee1dfc40fda33bb8d48fc72d4520ba9fc820c9123104a045206809037709f2a5f6723fa77d6bac5a573823d4ec3a7f1cb786a52ee2697e622e5d75962fa554d1024a6c355e21f33a63b2b72e6c4742a8b1c373aa532b40518c38c90b5373c2eb8c9d7be2a9e16047a3ee09dc9a6849deac5183ace6cfe91a9bef2ffc0a7df6ccebfd4c858c84b0e0355650d7466971e66f1e3883013e5ad1be33199b1d110b79070ac1b745ccb14cf63a08f8cca3a21c9525e626ff5f0c34746e10750fb742ad51f11f2acae3676c2111853d7250d01b77821a6ba9e04400ba2c543ca9f2d701ae6f47bfad14ffe3039ee9e71f7b2401359ade9938750ddb9c5a8b018a7929ed8d0e717ff1861446ce17535e9b17c187711190aae3388bd9490837a636c25ed4d42d7079ad1a51e13292c683d5d012abcf46965c534b83ab53f2c1f0cf5830ef7582e06863a33c19a70511df632885d63245965047ea96b56f1af5b3b94a54999f784fb9574fdfcd7c1230e07a2aaa04acd3097b2b9f8ddba05ae9734491deb5c1a513c76ed276cb78bbf4839dae3156d76af444a5805129d5df791167a9c8576a1d7f760b2d2797c4658669608706fbd0ace1be2346f74862dfc9ef518e55632e43c043186e5d070deb34d12fb9e5aba84e5cb50213dc88efd39cc35bf42455aa82d5e3b707b3140be3b8623b34fdd81d08615c188ae8438a13881fdf6bf32f2cb9ff5fa625561040c6b71d4b8eccc90bc3b99650d28dd1ee63773e49664e3d48c484996b290943635a6f2eb1ce9796d3fa144a3f00ef82faaa32d6a413668f7b521517cb68b2b017fcf56c79326fa5e4060e643631ca3f0a0dc0ed718798b6f46b130d437c33f64039e887324b6f5e604b1669d613923794edbf04b1b3caea54793b52b44b170173a4f25c7ecef3b71e2aad76e556b1cb9f1d637ec52ececfa950dd31dbb6a60828a3ad34c1beffe09eb4785786d63bad10a0b0f66ea88c57380f38ea85f018dbd7f538cf1ee7624095b9a01ec5edd528f281168af020609e651ff316aa1320a710134ddfca600cc72174dcdb846d2aa29916488aa1b537b66da92e61af526debef4eb38c984569eaf549ff2129449269b492d030cd74d885f6f5785881cc4804b4a8a09ba4ff7aefe9074ac7d0c4f05d51fe4cc0ff7388a772092b9d02d70e5433a5cf3e02f46a6bd6b818d59a07ce3b9fbbf8b5faba74563bcc5240930c2d406c9aaee3e3ce0429bf68ac2b0a57adb09414cff50817d2a48fb9fa624ab863cb0c31a8b8dc5eaf6fa68cc1d7c6c685c5a33edd5c8933b9e8ab628ee428d0743699b2ff17f25586c7ce959280bb0b8c5342251f0a30b53dbc7bf1ee426ac9619c3560f811f2268ee37f189794e2e4b3db3a2fb2e34b649e504fb467438abfd1082619cc4a0b30d66beb831077812e418d2e2148db10cf4d4a29101ca52ec445b8d83519dd7de85a98e0beae9ee537096d3f1a55a7a80cdfa93d25f07c9f98e8af18cde19ec1f99c5dd4588b717a5039ddb7f177717caf0d0fd45420a70dbd6d3146890d9e450d5224146db4c33b779e3c3a04b976c052bad042ac57dd38be45407808c0fb0d7e2a8819e6cd53c6739e6612996ddaa6f066552590aa0343bc1e62b298ff2514a0cef8be21956c2e942816f7a3a3a0935eaf9b37251409ce444c986c3817e82835555fe18239f3ae33469d7965c2bde9991fde556bd07af01df52bbde0c35bb4ef48e3b5d0db53f8ca4ed35b83f760f0a1bc4ed9f86e85d6039a17df373c85402ef956f01db00eb39c4b74bd0660d29ee746714d9780d738e05c6cca414ce3d7b40dda8036a9eea9ab1388805f913eb19bdd3f09d9e161eaa50231bd9caba61971f194332dd28c696a60458c1c6c2cc5da8b1192611c7c553e9e12fe48ce46bbb891be8bb118721c86222e671ddd1da8f0ccb2b68e02f2014b4925e904e88369aaf7466bd7033a60c265d45955944916ecbdb84bf1b522b01b0149c632e04c568a7eb627c5bb90ece052ebcf79166c28b30d23fe52da0a5ab5dea83ca479a3e3b7a9cfbbfea04dbe6137c19d067317c2ec427a8c75a6b06bec6dcd5d5c0edc9aa80b9003b8e17c088b2f3db327d3e42630d82d20120240c3ba56232280787da4aabbf5bc95a864029f00710e195f2a76460a0317d10b552fe1bea097e41d49756c680a41d6ac186e62169b6b6cd7776ea84618b5b752328a5bacaa10aa122ff9b2698b43efe73d852a899db644863c8c9bc8068ea86ea843fd6fe36272b91cdc5d5317083ef3fd1e5462a0b0d0604dc57b3bbfceb0fca4cd349625dd7b25166af30efe5ee6a0af953a74d65f4736c59918ee55a3b0d9d9d42e04c7f8a77e479109f740e20c464d5d7e3d16805f47b61f403ff7f408c9e850d9baacd8067e544536a4953480b0f9ee9cd45f41ebd67b51f78788a6470cb1e5ca72ca346ce8a50d0ca0c921d5576a4455a1afb6d0bc688004712ee122cacdb29c51e84893324c27fa4a3f1917edf5352272b4c97579a6152e4b77663d0ab532915f2eeb6a862de8b696452321b660c3f2449673d086e95a7af28845a5259b763e0fcd09f72acf7b6c811066263060e5aa5b24658e880a01fd56bda4dad5ab604e129290f7d5489728f2a40968c6168b21cebbbcd11727cc9e9160c4e92e04387d3b0d62aab06a61f26daedd9fed11816ef2180172a47f47184ac4032b88758c98a2e0fb200f70e93ba695f5ebb7a1029610ad360d3b7fa1b4640b9dc674d3625eef786da93dff19bc7991b5d6193a3896664763fde479b5dfc04812111a80782854f2cf68ca7d82765cc9eb40fba4b44640710ed6e653abf9f07b466333f4fd22784d53cf40e17120f42caa841eaa24056b237827b0f47f7257c103c35027e9f503e5acfd023e7357b600d3084d361d5ee65ba319b45c153212a54e6fed85af7e43e0a926ebcbc2edf8de7e2ec9528f00bec262ad04d5c9dafccaea06a24748d28bf1799bae0e895543084539c50b5aaa4fb50d7431d6f0c8cee2a54aaf7ee7919b55bf40adb688632e5dbe273cea09e97b19c3d8e1f4de000deb66fa1942ad03a62d3252f51992244366c156000b49c297167a6cbdedea7ebae139d295f0ad298e0864249b905b7eb812886ec70ecdb286702274b5b8574149bf3866f9e46b997ff5ed622b169a0eb071347f18d530db1663906a28f4544ee4e004ab87b65476af30ede118052ff052b8dc986ca2c93dd5d4943266a579c7698ea014f688b3e8063a107feb162d392e2177b01bff77fb5abe5feebd0607158049a5a093325b7c9ee6b4dfa7a9f65c7c2fb628920d3603a1c2dad979eaa047cd661a268af1078c9788d720e64e4ce9d12e68de1e417ef2f293323681e1071f9220e1ee43d2e29d111b870ce3439f5100ecd4551ab65ee74aa1667e564957e9bc0ae1ea193980da2a0ec2698073388c85bec25ef447f0d5e93a5203fa44dff268e5cb799ed3b66e63d5e07b487e7534f24934c73a62a243e0151843a0fd3807711a101eaa7fc71f0ba68aebb9534d57cba41b094eebfb4c31cca8eddfa426f676aa347be8a7023a4e91ddb154b35cd4d5f7dbc2e5db491de99f33fc2cff2d57029ac950e1ccd681980af6a4e8969dfe39b3c7bfcbcf8fac92f1e6ec9fe572bfa6a7d65860eab2ed10ac01a71290b52e3148e84b7376a8605cd2bb0e8681ffc54691ce087685e33921bd44d36c78291713dce17569570f62137e6904f0d68cf53aa2ec395c389a75141f08114fb293ea63950e4ffee55ec6fc83cf44876b8e7f25cdd393ff87b9eda6eb746085b61a6900de191f0ce2cb388d61ece52e78bc47368194e8e00277e0d1631e6b9d4626ef76f8522582ccd5a40be3febc699bb510acc6271d55ff0f4cf3bb7669855a72efd9ca3e1056a2fe592a5bc877cce2b1f63b58383971da87873d2d1349cf5881242cdce4e7e2c5c514755746a0e0a7c2a6d9701cde005ae3420beb17c379a3516662253554f51f0423bb1844b0b90c54ed8177ceb0e1036a6609d836e748ca06c40ca64befadc6443ec286a0ce464678e8d11eb455f7bb305acebf6cb1f50e394a9bfeb752df1687831bac9cdd811f4f112ef6658d0f8799a866374ff96c5e2b79f30e7a74f8a2bc9ed1f88f01f30e30cb78ffb2bff10108f35e910ee3be4463e9e6f0ed910e8d598326e71dfa2277ffe5579d7fe9b6018bfe295b25219eae07b3b0270665c3fa00c3e0d180812b5cd62925585de84a7c48a9a86dba96544a251654d1966e082432dc85b6149cf21e91a46020ec32b66d28ba3b6a90c0617bc6fdd55aea819af2bcf84864ad60c28fe3c9f8339d0aee68b39d97f63b6e082835d86119cf9b9fdc8b827c847ce40aa10e1577a710132316845e825345e95bdf94d0c66ec65a6c4319fce4792313663b5f7a651a6710783e6ab71608ac6cbbf3af6911adf596ccf7c172b9bd5bceb6db379967b32b143bdd11d2ee12ddf64ecef6391e0f8570e6cddd3db95204919362b89b739fa94e7c1bfde799fd5e22aa25ca6ca42e30c08e23aae2385d99ebab441072a880dcefdab74a4c9bd39d363f6d1933d59400fca161d432aa00f23b1b1c19a154be8989699d549b66d44e39896f5523443bc6ddf4a65e91f1f3fb7b52318869a05856a4fc92f3694c81ed833c972fb918f7e5\",\"cipherparams\":{\"iv\":\"8c46d6162cd4c765759aedcbce2a5874\"},\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"n\":262144,\"p\":1,\"r\":8,\"salt\":\"82fb6cdc6917609135277badacf15baa31899d08b71a5a0fa33167167c161537\"},\"mac\":\"9187b17f7eca48e6b8c586b0cd790dbe0feb876ac8385f93faa7d5e22a3c8fc7\"},\"id\":\"92caf6ee-2d43-48c0-859e-ffa1e0e23312\",\"version\":3}",
874
+ )};\nconst TEST_WALLET_PASSPHRASE = \"QuantumCoinExample123!\";\n\nfunction createTestWallet(provider) {\n // Caller must have called Initialize() first.\n return Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);\n}\n\nmodule.exports = {\n TEST_WALLET_ENCRYPTED_JSON,\n TEST_WALLET_PASSPHRASE,\n createTestWallet,\n};\n`,
875
+ );
876
+
714
877
  for (const a of artifacts) {
715
878
  _writeText(
716
879
  path.join(outDir, "test", "e2e", `${a.contractName}.e2e.test.js`),
@@ -718,74 +881,345 @@ async function main() {
718
881
  );
719
882
  }
720
883
 
884
+ if (artifacts.length > 1) {
885
+ _writeText(
886
+ path.join(outDir, "test", "e2e", "all-contracts.e2e.test.js"),
887
+ generateAllContractsTransactionalTestJs({ artifacts: artifacts.map((a) => ({ contractName: a.contractName, abi: a.abi })) }),
888
+ );
889
+ }
890
+
721
891
  if (artifacts.length === 1) {
722
892
  // Back-compat: keep original example filenames for a single-contract package.
723
893
  const a = artifacts[0];
724
894
  const ctor = _findConstructor(a.abi);
725
- const ctorArgsExpr = (ctor.inputs || []).map((i) => _solTypeToExampleValueExpr(i.type)).join(", ");
895
+ const ctorArgsExpr = (ctor.inputs || []).map((i) => _solTypeToExampleValueExpr(i)).join(", ");
726
896
 
727
897
  _writeText(
728
- path.join(outDir, "examples", "deploy.ts"),
729
- `import { Initialize } from "quantumcoin/config";\nimport { JsonRpcProvider, Wallet, encodeBytes32String } from "quantumcoin";\nimport { ${a.contractName}__factory } from "../src";\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL || "http://127.0.0.1:8545";\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const wallet = Wallet.createRandom().connect(provider);\n\n const factory = new ${a.contractName}__factory(wallet);\n const contract = await factory.deploy(${ctorArgsExpr}${ctorArgsExpr ? ", " : ""}{ gasLimit: 600000 });\n const tx = contract.deployTransaction();\n if (tx) await tx.wait();\n\n console.log("Deployed at:", contract.target);\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exitCode = 1;\n});\n`,
898
+ path.join(outDir, "examples", "deploy.js"),
899
+ `/**
900
+ * Deploy example (generated).
901
+ *
902
+ * Requires:
903
+ * - QC_RPC_URL env var
904
+ *
905
+ * WARNING: uses a hardcoded test wallet (funded) for convenience.
906
+ */
907
+ const { Initialize } = require("quantumcoin/config");
908
+ const { JsonRpcProvider, Wallet } = require("quantumcoin");
909
+ const { ${a.contractName}__factory } = require("..");
910
+
911
+ // Hardcoded test wallet (test-only; never use for real funds)
912
+ const TEST_WALLET_ENCRYPTED_JSON =
913
+ ${JSON.stringify(
914
+ "{\"address\":\"1a846abe71c8b989e8337c55d608be81c28ab3b2e40c83eaa2a68d516049aec6\",\"crypto\":{\"cipher\":\"aes-256-ctr\",\"ciphertext\":\"ab7e620dd66cb55ac201b9c6796de92bbb06f3681b5932eabe099871f1f7d79acabe30921a39ad13bfe74f42c515734882b6723760142aa3e26e011df514a534ae47bd15d86badd9c6f17c48d4c892711d54d441ee3a0ee0e5b060f816e79c7badd13ff4c235934b1986774223ecf6e8761388969bb239c759b54c8c70e6a2e27c93a4b70129c8159f461d271ae8f3573414c78b88e4d0abfa6365ed45456636d4ed971c7a0c6b84e6f0c2621e819268b135e2bcc169a54d1847b39e6ba2ae8ec969b69f330b7db9e785ed02204d5a1185915ae5338b0f40ef2a7f4d5aaf7563d502135e57f4eb89d5ec1efa5c77e374969d6cd85be625a2ed1225d68ecdd84067bfc69adb83ecd5c6050472eca28a5a646fcdd28077165c629975bec8a79fe1457cb53389b788b25e1f8eff8b2ca326d7dfcaba3f8839225a08057c018a458891fd2caa0d2b27632cffd80f592147ccec9a10dc8a08a48fb55047bff5cf85cda39eb089096bef63842fc3686412f298a54a9e4b0bf4ad36907ba373cbd6d32e7ac494af371da5aa9d38a3463220865114c4adc5e4ac258ba9c6af9fa2ddfd1aec2e16887e4b3977c69561df8599ac9d411c9dd2a4d57f92ea4e5c02aae3f49fb3bc83e16673e6c2dbe96bb181c8dfd0f9757ade2e4ff27215a836058c5ffeab042f6f97c7c02339f76a6284680e01b4bb733690eb3347fbfcc26614b8bf755f9dfce3fea9d4e4d15b164983201732c2e87593a86bca6da6972e128490338f76ae68135888070f4e59e90db54d23834769bdbda9769213faf5357f9167a224523975a946367b68f0cec98658575609f58bfd329e420a921c06713326e4cb20a0df1d77f37e78a320a637a96c604ca3fa89e24beb42313751b8f09b14f9c14c77e4fd13fc6382505d27c771bca0d821ec7c3765acffa99d83c50140a56b0b28101c762bd682fe55cb6f23cbeb3f421d7b36021010e45ac27160dd7ead99c864a1b550c7edb1246950fe32dcc049799f9085287f0a747a6ef7a023df46a23a22f3e833bbf8d404f84344870492658256ee1dfc40fda33bb8d48fc72d4520ba9fc820c9123104a045206809037709f2a5f6723fa77d6bac5a573823d4ec3a7f1cb786a52ee2697e622e5d75962fa554d1024a6c355e21f33a63b2b72e6c4742a8b1c373aa532b40518c38c90b5373c2eb8c9d7be2a9e16047a3ee09dc9a6849deac5183ace6cfe91a9bef2ffc0a7df6ccebfd4c858c84b0e0355650d7466971e66f1e3883013e5ad1be33199b1d110b79070ac1b745ccb14cf63a08f8cca3a21c9525e626ff5f0c34746e10750fb742ad51f11f2acae3676c2111853d7250d01b77821a6ba9e04400ba2c543ca9f2d701ae6f47bfad14ffe3039ee9e71f7b2401359ade9938750ddb9c5a8b018a7929ed8d0e717ff1861446ce17535e9b17c187711190aae3388bd9490837a636c25ed4d42d7079ad1a51e13292c683d5d012abcf46965c534b83ab53f2c1f0cf5830ef7582e06863a33c19a70511df632885d63245965047ea96b56f1af5b3b94a54999f784fb9574fdfcd7c1230e07a2aaa04acd3097b2b9f8ddba05ae9734491deb5c1a513c76ed276cb78bbf4839dae3156d76af444a5805129d5df791167a9c8576a1d7f760b2d2797c4658669608706fbd0ace1be2346f74862dfc9ef518e55632e43c043186e5d070deb34d12fb9e5aba84e5cb50213dc88efd39cc35bf42455aa82d5e3b707b3140be3b8623b34fdd81d08615c188ae8438a13881fdf6bf32f2cb9ff5fa625561040c6b71d4b8eccc90bc3b99650d28dd1ee63773e49664e3d48c484996b290943635a6f2eb1ce9796d3fa144a3f00ef82faaa32d6a413668f7b521517cb68b2b017fcf56c79326fa5e4060e643631ca3f0a0dc0ed718798b6f46b130d437c33f64039e887324b6f5e604b1669d613923794edbf04b1b3caea54793b52b44b170173a4f25c7ecef3b71e2aad76e556b1cb9f1d637ec52ececfa950dd31dbb6a60828a3ad34c1beffe09eb4785786d63bad10a0b0f66ea88c57380f38ea85f018dbd7f538cf1ee7624095b9a01ec5edd528f281168af020609e651ff316aa1320a710134ddfca600cc72174dcdb846d2aa29916488aa1b537b66da92e61af526debef4eb38c984569eaf549ff2129449269b492d030cd74d885f6f5785881cc4804b4a8a09ba4ff7aefe9074ac7d0c4f05d51fe4cc0ff7388a772092b9d02d70e5433a5cf3e02f46a6bd6b818d59a07ce3b9fbbf8b5faba74563bcc5240930c2d406c9aaee3e3ce0429bf68ac2b0a57adb09414cff50817d2a48fb9fa624ab863cb0c31a8b8dc5eaf6fa68cc1d7c6c685c5a33edd5c8933b9e8ab628ee428d0743699b2ff17f25586c7ce959280bb0b8c5342251f0a30b53dbc7bf1ee426ac9619c3560f811f2268ee37f189794e2e4b3db3a2fb2e34b649e504fb467438abfd1082619cc4a0b30d66beb831077812e418d2e2148db10cf4d4a29101ca52ec445b8d83519dd7de85a98e0beae9ee537096d3f1a55a7a80cdfa93d25f07c9f98e8af18cde19ec1f99c5dd4588b717a5039ddb7f177717caf0d0fd45420a70dbd6d3146890d9e450d5224146db4c33b779e3c3a04b976c052bad042ac57dd38be45407808c0fb0d7e2a8819e6cd53c6739e6612996ddaa6f066552590aa0343bc1e62b298ff2514a0cef8be21956c2e942816f7a3a3a0935eaf9b37251409ce444c986c3817e82835555fe18239f3ae33469d7965c2bde9991fde556bd07af01df52bbde0c35bb4ef48e3b5d0db53f8ca4ed35b83f760f0a1bc4ed9f86e85d6039a17df373c85402ef956f01db00eb39c4b74bd0660d29ee746714d9780d738e05c6cca414ce3d7b40dda8036a9eea9ab1388805f913eb19bdd3f09d9e161eaa50231bd9caba61971f194332dd28c696a60458c1c6c2cc5da8b1192611c7c553e9e12fe48ce46bbb891be8bb118721c86222e671ddd1da8f0ccb2b68e02f2014b4925e904e88369aaf7466bd7033a60c265d45955944916ecbdb84bf1b522b01b0149c632e04c568a7eb627c5bb90ece052ebcf79166c28b30d23fe52da0a5ab5dea83ca479a3e3b7a9cfbbfea04dbe6137c19d067317c2ec427a8c75a6b06bec6dcd5d5c0edc9aa80b9003b8e17c088b2f3db327d3e42630d82d20120240c3ba56232280787da4aabbf5bc95a864029f00710e195f2a76460a0317d10b552fe1bea097e41d49756c680a41d6ac186e62169b6b6cd7776ea84618b5b752328a5bacaa10aa122ff9b2698b43efe73d852a899db644863c8c9bc8068ea86ea843fd6fe36272b91cdc5d5317083ef3fd1e5462a0b0d0604dc57b3bbfceb0fca4cd349625dd7b25166af30efe5ee6a0af953a74d65f4736c59918ee55a3b0d9d9d42e04c7f8a77e479109f740e20c464d5d7e3d16805f47b61f403ff7f408c9e850d9baacd8067e544536a4953480b0f9ee9cd45f41ebd67b51f78788a6470cb1e5ca72ca346ce8a50d0ca0c921d5576a4455a1afb6d0bc688004712ee122cacdb29c51e84893324c27fa4a3f1917edf5352272b4c97579a6152e4b77663d0ab532915f2eeb6a862de8b696452321b660c3f2449673d086e95a7af28845a5259b763e0fcd09f72acf7b6c811066263060e5aa5b24658e880a01fd56bda4dad5ab604e129290f7d5489728f2a40968c6168b21cebbbcd11727cc9e9160c4e92e04387d3b0d62aab06a61f26daedd9fed11816ef2180172a47f47184ac4032b88758c98a2e0fb200f70e93ba695f5ebb7a1029610ad360d3b7fa1b4640b9dc674d3625eef786da93dff19bc7991b5d6193a3896664763fde479b5dfc04812111a80782854f2cf68ca7d82765cc9eb40fba4b44640710ed6e653abf9f07b466333f4fd22784d53cf40e17120f42caa841eaa24056b237827b0f47f7257c103c35027e9f503e5acfd023e7357b600d3084d361d5ee65ba319b45c153212a54e6fed85af7e43e0a926ebcbc2edf8de7e2ec9528f00bec262ad04d5c9dafccaea06a24748d28bf1799bae0e895543084539c50b5aaa4fb50d7431d6f0c8cee2a54aaf7ee7919b55bf40adb688632e5dbe273cea09e97b19c3d8e1f4de000deb66fa1942ad03a62d3252f51992244366c156000b49c297167a6cbdedea7ebae139d295f0ad298e0864249b905b7eb812886ec70ecdb286702274b5b8574149bf3866f9e46b997ff5ed622b169a0eb071347f18d530db1663906a28f4544ee4e004ab87b65476af30ede118052ff052b8dc986ca2c93dd5d4943266a579c7698ea014f688b3e8063a107feb162d392e2177b01bff77fb5abe5feebd0607158049a5a093325b7c9ee6b4dfa7a9f65c7c2fb628920d3603a1c2dad979eaa047cd661a268af1078c9788d720e64e4ce9d12e68de1e417ef2f293323681e1071f9220e1ee43d2e29d111b870ce3439f5100ecd4551ab65ee74aa1667e564957e9bc0ae1ea193980da2a0ec2698073388c85bec25ef447f0d5e93a5203fa44dff268e5cb799ed3b66e63d5e07b487e7534f24934c73a62a243e0151843a0fd3807711a101eaa7fc71f0ba68aebb9534d57cba41b094eebfb4c31cca8eddfa426f676aa347be8a7023a4e91ddb154b35cd4d5f7dbc2e5db491de99f33fc2cff2d57029ac950e1ccd681980af6a4e8969dfe39b3c7bfcbcf8fac92f1e6ec9fe572bfa6a7d65860eab2ed10ac01a71290b52e3148e84b7376a8605cd2bb0e8681ffc54691ce087685e33921bd44d36c78291713dce17569570f62137e6904f0d68cf53aa2ec395c389a75141f08114fb293ea63950e4ffee55ec6fc83cf44876b8e7f25cdd393ff87b9eda6eb746085b61a6900de191f0ce2cb388d61ece52e78bc47368194e8e00277e0d1631e6b9d4626ef76f8522582ccd5a40be3febc699bb510acc6271d55ff0f4cf3bb7669855a72efd9ca3e1056a2fe592a5bc877cce2b1f63b58383971da87873d2d1349cf5881242cdce4e7e2c5c514755746a0e0a7c2a6d9701cde005ae3420beb17c379a3516662253554f51f0423bb1844b0b90c54ed8177ceb0e1036a6609d836e748ca06c40ca64befadc6443ec286a0ce464678e8d11eb455f7bb305acebf6cb1f50e394a9bfeb752df1687831bac9cdd811f4f112ef6658d0f8799a866374ff96c5e2b79f30e7a74f8a2bc9ed1f88f01f30e30cb78ffb2bff10108f35e910ee3be4463e9e6f0ed910e8d598326e71dfa2277ffe5579d7fe9b6018bfe295b25219eae07b3b0270665c3fa00c3e0d180812b5cd62925585de84a7c48a9a86dba96544a251654d1966e082432dc85b6149cf21e91a46020ec32b66d28ba3b6a90c0617bc6fdd55aea819af2bcf84864ad60c28fe3c9f8339d0aee68b39d97f63b6e082835d86119cf9b9fdc8b827c847ce40aa10e1577a710132316845e825345e95bdf94d0c66ec65a6c4319fce4792313663b5f7a651a6710783e6ab71608ac6cbbf3af6911adf596ccf7c172b9bd5bceb6db379967b32b143bdd11d2ee12ddf64ecef6391e0f8570e6cddd3db95204919362b89b739fa94e7c1bfde799fd5e22aa25ca6ca42e30c08e23aae2385d99ebab441072a880dcefdab74a4c9bd39d363f6d1933d59400fca161d432aa00f23b1b1c19a154be8989699d549b66d44e39896f5523443bc6ddf4a65e91f1f3fb7b52318869a05856a4fc92f3694c81ed833c972fb918f7e5\",\"cipherparams\":{\"iv\":\"8c46d6162cd4c765759aedcbce2a5874\"},\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"n\":262144,\"p\":1,\"r\":8,\"salt\":\"82fb6cdc6917609135277badacf15baa31899d08b71a5a0fa33167167c161537\"},\"mac\":\"9187b17f7eca48e6b8c586b0cd790dbe0feb876ac8385f93faa7d5e22a3c8fc7\"},\"id\":\"92caf6ee-2d43-48c0-859e-ffa1e0e23312\",\"version\":3}",
915
+ )};
916
+ const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
917
+
918
+ async function main() {
919
+ const rpcUrl = process.env.QC_RPC_URL;
920
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
921
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
922
+ await Initialize(null);
923
+
924
+ const provider = new JsonRpcProvider(rpcUrl, chainId);
925
+ const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
926
+
927
+ const factory = new ${a.contractName}__factory(wallet);
928
+ const contract = await factory.deploy(${ctorArgsExpr}${ctorArgsExpr ? ", " : ""}{ gasLimit: 600000 });
929
+ const tx = contract.deployTransaction();
930
+ if (tx) await tx.wait(1, 600_000);
931
+
932
+ console.log("Deployed at:", contract.target);
933
+ console.log("Next:");
934
+ console.log(' $env:CONTRACT_ADDRESS="' + contract.target + '"');
935
+ console.log(" node examples/read-operations.js");
936
+ }
937
+
938
+ main().catch((e) => {
939
+ console.error(e);
940
+ process.exitCode = 1;
941
+ });
942
+ `,
730
943
  );
731
944
 
732
945
  _writeText(
733
- path.join(outDir, "examples", "read-operations.ts"),
734
- `import { Initialize } from "quantumcoin/config";\nimport { JsonRpcProvider } from "quantumcoin";\nimport { ${a.contractName} } from "../src";\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL || "http://127.0.0.1:8545";\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n const address = process.env.CONTRACT_ADDRESS || "0x...";\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const contract = ${a.contractName}.connect(address, provider);\n\n console.log("Contract:", contract.target);\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exitCode = 1;\n});\n`,
946
+ path.join(outDir, "examples", "read-operations.js"),
947
+ `/**
948
+ * Read operations example (generated).
949
+ *
950
+ * Requires:
951
+ * - QC_RPC_URL env var
952
+ * - CONTRACT_ADDRESS env var
953
+ */
954
+ const { Initialize } = require("quantumcoin/config");
955
+ const { JsonRpcProvider } = require("quantumcoin");
956
+ const { ${a.contractName} } = require("..");
957
+
958
+ async function main() {
959
+ const rpcUrl = process.env.QC_RPC_URL;
960
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
961
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
962
+ const address = process.env.CONTRACT_ADDRESS;
963
+ if (!address) throw new Error("CONTRACT_ADDRESS is required");
964
+ await Initialize(null);
965
+
966
+ const provider = new JsonRpcProvider(rpcUrl, chainId);
967
+ const contract = ${a.contractName}.connect(address, provider);
968
+
969
+ console.log("Contract:", contract.target);
970
+
971
+ if (typeof contract.name === "function") {
972
+ console.log("name():", await contract.name());
973
+ }
974
+ if (typeof contract.symbol === "function") {
975
+ console.log("symbol():", await contract.symbol());
976
+ }
977
+ if (typeof contract.totalSupply === "function") {
978
+ // Generated wrappers already unwrap single-return values to a hard type.
979
+ const v = await contract.totalSupply();
980
+ console.log("totalSupply():", v.toString());
981
+ }
982
+ if (typeof contract.balanceOf === "function" && process.env.WALLET_ADDRESS) {
983
+ // Generated wrappers already unwrap single-return values to a hard type.
984
+ const v = await contract.balanceOf(process.env.WALLET_ADDRESS);
985
+ console.log("balanceOf(WALLET_ADDRESS):", v.toString());
986
+ }
987
+ }
988
+
989
+ main().catch((e) => {
990
+ console.error(e);
991
+ process.exitCode = 1;
992
+ });
993
+ `,
735
994
  );
736
995
 
737
996
  _writeText(
738
- path.join(outDir, "examples", "write-operations.ts"),
739
- `import { Initialize } from "quantumcoin/config";\nimport { JsonRpcProvider, Wallet } from "quantumcoin";\nimport { ${a.contractName} } from "../src";\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL || "http://127.0.0.1:8545";\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n const address = process.env.CONTRACT_ADDRESS || "0x...";\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const wallet = Wallet.createRandom().connect(provider);\n const contract = ${a.contractName}.connect(address, wallet);\n\n // const tx = await contract.someWriteMethod(/* args */);\n // await tx.wait();\n console.log("Done");\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exitCode = 1;\n});\n`,
997
+ path.join(outDir, "examples", "write-operations.js"),
998
+ `/**
999
+ * Write operations example (generated).
1000
+ *
1001
+ * Requires:
1002
+ * - QC_RPC_URL env var
1003
+ * - CONTRACT_ADDRESS env var
1004
+ *
1005
+ * WARNING: uses a hardcoded test wallet (funded) for convenience.
1006
+ */
1007
+ const { Initialize } = require("quantumcoin/config");
1008
+ const { JsonRpcProvider, Wallet } = require("quantumcoin");
1009
+ const { ${a.contractName} } = require("..");
1010
+
1011
+ // Hardcoded test wallet (test-only; never use for real funds)
1012
+ const TEST_WALLET_ENCRYPTED_JSON =
1013
+ ${JSON.stringify(
1014
+ "{\"address\":\"1a846abe71c8b989e8337c55d608be81c28ab3b2e40c83eaa2a68d516049aec6\",\"crypto\":{\"cipher\":\"aes-256-ctr\",\"ciphertext\":\"ab7e620dd66cb55ac201b9c6796de92bbb06f3681b5932eabe099871f1f7d79acabe30921a39ad13bfe74f42c515734882b6723760142aa3e26e011df514a534ae47bd15d86badd9c6f17c48d4c892711d54d441ee3a0ee0e5b060f816e79c7badd13ff4c235934b1986774223ecf6e8761388969bb239c759b54c8c70e6a2e27c93a4b70129c8159f461d271ae8f3573414c78b88e4d0abfa6365ed45456636d4ed971c7a0c6b84e6f0c2621e819268b135e2bcc169a54d1847b39e6ba2ae8ec969b69f330b7db9e785ed02204d5a1185915ae5338b0f40ef2a7f4d5aaf7563d502135e57f4eb89d5ec1efa5c77e374969d6cd85be625a2ed1225d68ecdd84067bfc69adb83ecd5c6050472eca28a5a646fcdd28077165c629975bec8a79fe1457cb53389b788b25e1f8eff8b2ca326d7dfcaba3f8839225a08057c018a458891fd2caa0d2b27632cffd80f592147ccec9a10dc8a08a48fb55047bff5cf85cda39eb089096bef63842fc3686412f298a54a9e4b0bf4ad36907ba373cbd6d32e7ac494af371da5aa9d38a3463220865114c4adc5e4ac258ba9c6af9fa2ddfd1aec2e16887e4b3977c69561df8599ac9d411c9dd2a4d57f92ea4e5c02aae3f49fb3bc83e16673e6c2dbe96bb181c8dfd0f9757ade2e4ff27215a836058c5ffeab042f6f97c7c02339f76a6284680e01b4bb733690eb3347fbfcc26614b8bf755f9dfce3fea9d4e4d15b164983201732c2e87593a86bca6da6972e128490338f76ae68135888070f4e59e90db54d23834769bdbda9769213faf5357f9167a224523975a946367b68f0cec98658575609f58bfd329e420a921c06713326e4cb20a0df1d77f37e78a320a637a96c604ca3fa89e24beb42313751b8f09b14f9c14c77e4fd13fc6382505d27c771bca0d821ec7c3765acffa99d83c50140a56b0b28101c762bd682fe55cb6f23cbeb3f421d7b36021010e45ac27160dd7ead99c864a1b550c7edb1246950fe32dcc049799f9085287f0a747a6ef7a023df46a23a22f3e833bbf8d404f84344870492658256ee1dfc40fda33bb8d48fc72d4520ba9fc820c9123104a045206809037709f2a5f6723fa77d6bac5a573823d4ec3a7f1cb786a52ee2697e622e5d75962fa554d1024a6c355e21f33a63b2b72e6c4742a8b1c373aa532b40518c38c90b5373c2eb8c9d7be2a9e16047a3ee09dc9a6849deac5183ace6cfe91a9bef2ffc0a7df6ccebfd4c858c84b0e0355650d7466971e66f1e3883013e5ad1be33199b1d110b79070ac1b745ccb14cf63a08f8cca3a21c9525e626ff5f0c34746e10750fb742ad51f11f2acae3676c2111853d7250d01b77821a6ba9e04400ba2c543ca9f2d701ae6f47bfad14ffe3039ee9e71f7b2401359ade9938750ddb9c5a8b018a7929ed8d0e717ff1861446ce17535e9b17c187711190aae3388bd9490837a636c25ed4d42d7079ad1a51e13292c683d5d012abcf46965c534b83ab53f2c1f0cf5830ef7582e06863a33c19a70511df632885d63245965047ea96b56f1af5b3b94a54999f784fb9574fdfcd7c1230e07a2aaa04acd3097b2b9f8ddba05ae9734491deb5c1a513c76ed276cb78bbf4839dae3156d76af444a5805129d5df791167a9c8576a1d7f760b2d2797c4658669608706fbd0ace1be2346f74862dfc9ef518e55632e43c043186e5d070deb34d12fb9e5aba84e5cb50213dc88efd39cc35bf42455aa82d5e3b707b3140be3b8623b34fdd81d08615c188ae8438a13881fdf6bf32f2cb9ff5fa625561040c6b71d4b8eccc90bc3b99650d28dd1ee63773e49664e3d48c484996b290943635a6f2eb1ce9796d3fa144a3f00ef82faaa32d6a413668f7b521517cb68b2b017fcf56c79326fa5e4060e643631ca3f0a0dc0ed718798b6f46b130d437c33f64039e887324b6f5e604b1669d613923794edbf04b1b3caea54793b52b44b170173a4f25c7ecef3b71e2aad76e556b1cb9f1d637ec52ececfa950dd31dbb6a60828a3ad34c1beffe09eb4785786d63bad10a0b0f66ea88c57380f38ea85f018dbd7f538cf1ee7624095b9a01ec5edd528f281168af020609e651ff316aa1320a710134ddfca600cc72174dcdb846d2aa29916488aa1b537b66da92e61af526debef4eb38c984569eaf549ff2129449269b492d030cd74d885f6f5785881cc4804b4a8a09ba4ff7aefe9074ac7d0c4f05d51fe4cc0ff7388a772092b9d02d70e5433a5cf3e02f46a6bd6b818d59a07ce3b9fbbf8b5faba74563bcc5240930c2d406c9aaee3e3ce0429bf68ac2b0a57adb09414cff50817d2a48fb9fa624ab863cb0c31a8b8dc5eaf6fa68cc1d7c6c685c5a33edd5c8933b9e8ab628ee428d0743699b2ff17f25586c7ce959280bb0b8c5342251f0a30b53dbc7bf1ee426ac9619c3560f811f2268ee37f189794e2e4b3db3a2fb2e34b649e504fb467438abfd1082619cc4a0b30d66beb831077812e418d2e2148db10cf4d4a29101ca52ec445b8d83519dd7de85a98e0beae9ee537096d3f1a55a7a80cdfa93d25f07c9f98e8af18cde19ec1f99c5dd4588b717a5039ddb7f177717caf0d0fd45420a70dbd6d3146890d9e450d5224146db4c33b779e3c3a04b976c052bad042ac57dd38be45407808c0fb0d7e2a8819e6cd53c6739e6612996ddaa6f066552590aa0343bc1e62b298ff2514a0cef8be21956c2e942816f7a3a3a0935eaf9b37251409ce444c986c3817e82835555fe18239f3ae33469d7965c2bde9991fde556bd07af01df52bbde0c35bb4ef48e3b5d0db53f8ca4ed35b83f760f0a1bc4ed9f86e85d6039a17df373c85402ef956f01db00eb39c4b74bd0660d29ee746714d9780d738e05c6cca414ce3d7b40dda8036a9eea9ab1388805f913eb19bdd3f09d9e161eaa50231bd9caba61971f194332dd28c696a60458c1c6c2cc5da8b1192611c7c553e9e12fe48ce46bbb891be8bb118721c86222e671ddd1da8f0ccb2b68e02f2014b4925e904e88369aaf7466bd7033a60c265d45955944916ecbdb84bf1b522b01b0149c632e04c568a7eb627c5bb90ece052ebcf79166c28b30d23fe52da0a5ab5dea83ca479a3e3b7a9cfbbfea04dbe6137c19d067317c2ec427a8c75a6b06bec6dcd5d5c0edc9aa80b9003b8e17c088b2f3db327d3e42630d82d20120240c3ba56232280787da4aabbf5bc95a864029f00710e195f2a76460a0317d10b552fe1bea097e41d49756c680a41d6ac186e62169b6b6cd7776ea84618b5b752328a5bacaa10aa122ff9b2698b43efe73d852a899db644863c8c9bc8068ea86ea843fd6fe36272b91cdc5d5317083ef3fd1e5462a0b0d0604dc57b3bbfceb0fca4cd349625dd7b25166af30efe5ee6a0af953a74d65f4736c59918ee55a3b0d9d9d42e04c7f8a77e479109f740e20c464d5d7e3d16805f47b61f403ff7f408c9e850d9baacd8067e544536a4953480b0f9ee9cd45f41ebd67b51f78788a6470cb1e5ca72ca346ce8a50d0ca0c921d5576a4455a1afb6d0bc688004712ee122cacdb29c51e84893324c27fa4a3f1917edf5352272b4c97579a6152e4b77663d0ab532915f2eeb6a862de8b696452321b660c3f2449673d086e95a7af28845a5259b763e0fcd09f72acf7b6c811066263060e5aa5b24658e880a01fd56bda4dad5ab604e129290f7d5489728f2a40968c6168b21cebbbcd11727cc9e9160c4e92e04387d3b0d62aab06a61f26daedd9fed11816ef2180172a47f47184ac4032b88758c98a2e0fb200f70e93ba695f5ebb7a1029610ad360d3b7fa1b4640b9dc674d3625eef786da93dff19bc7991b5d6193a3896664763fde479b5dfc04812111a80782854f2cf68ca7d82765cc9eb40fba4b44640710ed6e653abf9f07b466333f4fd22784d53cf40e17120f42caa841eaa24056b237827b0f47f7257c103c35027e9f503e5acfd023e7357b600d3084d361d5ee65ba319b45c153212a54e6fed85af7e43e0a926ebcbc2edf8de7e2ec9528f00bec262ad04d5c9dafccaea06a24748d28bf1799bae0e895543084539c50b5aaa4fb50d7431d6f0c8cee2a54aaf7ee7919b55bf40adb688632e5dbe273cea09e97b19c3d8e1f4de000deb66fa1942ad03a62d3252f51992244366c156000b49c297167a6cbdedea7ebae139d295f0ad298e0864249b905b7eb812886ec70ecdb286702274b5b8574149bf3866f9e46b997ff5ed622b169a0eb071347f18d530db1663906a28f4544ee4e004ab87b65476af30ede118052ff052b8dc986ca2c93dd5d4943266a579c7698ea014f688b3e8063a107feb162d392e2177b01bff77fb5abe5feebd0607158049a5a093325b7c9ee6b4dfa7a9f65c7c2fb628920d3603a1c2dad979eaa047cd661a268af1078c9788d720e64e4ce9d12e68de1e417ef2f293323681e1071f9220e1ee43d2e29d111b870ce3439f5100ecd4551ab65ee74aa1667e564957e9bc0ae1ea193980da2a0ec2698073388c85bec25ef447f0d5e93a5203fa44dff268e5cb799ed3b66e63d5e07b487e7534f24934c73a62a243e0151843a0fd3807711a101eaa7fc71f0ba68aebb9534d57cba41b094eebfb4c31cca8eddfa426f676aa347be8a7023a4e91ddb154b35cd4d5f7dbc2e5db491de99f33fc2cff2d57029ac950e1ccd681980af6a4e8969dfe39b3c7bfcbcf8fac92f1e6ec9fe572bfa6a7d65860eab2ed10ac01a71290b52e3148e84b7376a8605cd2bb0e8681ffc54691ce087685e33921bd44d36c78291713dce17569570f62137e6904f0d68cf53aa2ec395c389a75141f08114fb293ea63950e4ffee55ec6fc83cf44876b8e7f25cdd393ff87b9eda6eb746085b61a6900de191f0ce2cb388d61ece52e78bc47368194e8e00277e0d1631e6b9d4626ef76f8522582ccd5a40be3febc699bb510acc6271d55ff0f4cf3bb7669855a72efd9ca3e1056a2fe592a5bc877cce2b1f63b58383971da87873d2d1349cf5881242cdce4e7e2c5c514755746a0e0a7c2a6d9701cde005ae3420beb17c379a3516662253554f51f0423bb1844b0b90c54ed8177ceb0e1036a6609d836e748ca06c40ca64befadc6443ec286a0ce464678e8d11eb455f7bb305acebf6cb1f50e394a9bfeb752df1687831bac9cdd811f4f112ef6658d0f8799a866374ff96c5e2b79f30e7a74f8a2bc9ed1f88f01f30e30cb78ffb2bff10108f35e910ee3be4463e9e6f0ed910e8d598326e71dfa2277ffe5579d7fe9b6018bfe295b25219eae07b3b0270665c3fa00c3e0d180812b5cd62925585de84a7c48a9a86dba96544a251654d1966e082432dc85b6149cf21e91a46020ec32b66d28ba3b6a90c0617bc6fdd55aea819af2bcf84864ad60c28fe3c9f8339d0aee68b39d97f63b6e082835d86119cf9b9fdc8b827c847ce40aa10e1577a710132316845e825345e95bdf94d0c66ec65a6c4319fce4792313663b5f7a651a6710783e6ab71608ac6cbbf3af6911adf596ccf7c172b9bd5bceb6db379967b32b143bdd11d2ee12ddf64ecef6391e0f8570e6cddd3db95204919362b89b739fa94e7c1bfde799fd5e22aa25ca6ca42e30c08e23aae2385d99ebab441072a880dcefdab74a4c9bd39d363f6d1933d59400fca161d432aa00f23b1b1c19a154be8989699d549b66d44e39896f5523443bc6ddf4a65e91f1f3fb7b52318869a05856a4fc92f3694c81ed833c972fb918f7e5\",\"cipherparams\":{\"iv\":\"8c46d6162cd4c765759aedcbce2a5874\"},\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"n\":262144,\"p\":1,\"r\":8,\"salt\":\"82fb6cdc6917609135277badacf15baa31899d08b71a5a0fa33167167c161537\"},\"mac\":\"9187b17f7eca48e6b8c586b0cd790dbe0feb876ac8385f93faa7d5e22a3c8fc7\"},\"id\":\"92caf6ee-2d43-48c0-859e-ffa1e0e23312\",\"version\":3}",
1015
+ )};
1016
+ const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
1017
+
1018
+ async function main() {
1019
+ const rpcUrl = process.env.QC_RPC_URL;
1020
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
1021
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1022
+ const address = process.env.CONTRACT_ADDRESS;
1023
+ if (!address) throw new Error("CONTRACT_ADDRESS is required");
1024
+ await Initialize(null);
1025
+
1026
+ const provider = new JsonRpcProvider(rpcUrl, chainId);
1027
+ const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
1028
+ const contract = ${a.contractName}.connect(address, wallet);
1029
+
1030
+ if (typeof contract.approve === "function") {
1031
+ const tx = await contract.approve(wallet.address, 123, { gasLimit: 200000 });
1032
+ await tx.wait(1, 600_000);
1033
+ console.log("approve(wallet.address, 123) succeeded");
1034
+ return;
1035
+ }
1036
+
1037
+ console.log("No known write method template for this ABI.");
1038
+ }
1039
+
1040
+ main().catch((e) => {
1041
+ console.error(e);
1042
+ process.exitCode = 1;
1043
+ });
1044
+ `,
740
1045
  );
741
1046
 
742
1047
  _writeText(
743
- path.join(outDir, "examples", "events.ts"),
744
- `import { Initialize } from "quantumcoin/config";\nimport { JsonRpcProvider } from "quantumcoin";\nimport { ${a.contractName} } from "../src";\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL || "http://127.0.0.1:8545";\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n const address = process.env.CONTRACT_ADDRESS || "0x...";\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const contract = ${a.contractName}.connect(address, provider);\n\n console.log("Contract:", contract.target);\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exitCode = 1;\n});\n`,
1048
+ path.join(outDir, "examples", "offline-signing.js"),
1049
+ `/**
1050
+ * Offline signing example (generated).
1051
+ *
1052
+ * Demonstrates:
1053
+ * - ${a.contractName}__factory.getDeployTransaction(...)
1054
+ * - contract.populateTransaction.<method>(...)
1055
+ * - wallet.signTransaction(txReq) (offline) + provider.sendRawTransaction(rawTx)
1056
+ *
1057
+ * Requires:
1058
+ * - QC_RPC_URL env var
1059
+ *
1060
+ * WARNING: uses a hardcoded test wallet (funded) for convenience.
1061
+ */
1062
+ const { Initialize } = require("quantumcoin/config");
1063
+ const { JsonRpcProvider, Wallet, getCreateAddress } = require("quantumcoin");
1064
+ const { ${a.contractName}, ${a.contractName}__factory } = require("..");
1065
+
1066
+ // Hardcoded test wallet (test-only; never use for real funds)
1067
+ const TEST_WALLET_ENCRYPTED_JSON =
1068
+ ${JSON.stringify(
1069
+ "{\"address\":\"1a846abe71c8b989e8337c55d608be81c28ab3b2e40c83eaa2a68d516049aec6\",\"crypto\":{\"cipher\":\"aes-256-ctr\",\"ciphertext\":\"ab7e620dd66cb55ac201b9c6796de92bbb06f3681b5932eabe099871f1f7d79acabe30921a39ad13bfe74f42c515734882b6723760142aa3e26e011df514a534ae47bd15d86badd9c6f17c48d4c892711d54d441ee3a0ee0e5b060f816e79c7badd13ff4c235934b1986774223ecf6e8761388969bb239c759b54c8c70e6a2e27c93a4b70129c8159f461d271ae8f3573414c78b88e4d0abfa6365ed45456636d4ed971c7a0c6b84e6f0c2621e819268b135e2bcc169a54d1847b39e6ba2ae8ec969b69f330b7db9e785ed02204d5a1185915ae5338b0f40ef2a7f4d5aaf7563d502135e57f4eb89d5ec1efa5c77e374969d6cd85be625a2ed1225d68ecdd84067bfc69adb83ecd5c6050472eca28a5a646fcdd28077165c629975bec8a79fe1457cb53389b788b25e1f8eff8b2ca326d7dfcaba3f8839225a08057c018a458891fd2caa0d2b27632cffd80f592147ccec9a10dc8a08a48fb55047bff5cf85cda39eb089096bef63842fc3686412f298a54a9e4b0bf4ad36907ba373cbd6d32e7ac494af371da5aa9d38a3463220865114c4adc5e4ac258ba9c6af9fa2ddfd1aec2e16887e4b3977c69561df8599ac9d411c9dd2a4d57f92ea4e5c02aae3f49fb3bc83e16673e6c2dbe96bb181c8dfd0f9757ade2e4ff27215a836058c5ffeab042f6f97c7c02339f76a6284680e01b4bb733690eb3347fbfcc26614b8bf755f9dfce3fea9d4e4d15b164983201732c2e87593a86bca6da6972e128490338f76ae68135888070f4e59e90db54d23834769bdbda9769213faf5357f9167a224523975a946367b68f0cec98658575609f58bfd329e420a921c06713326e4cb20a0df1d77f37e78a320a637a96c604ca3fa89e24beb42313751b8f09b14f9c14c77e4fd13fc6382505d27c771bca0d821ec7c3765acffa99d83c50140a56b0b28101c762bd682fe55cb6f23cbeb3f421d7b36021010e45ac27160dd7ead99c864a1b550c7edb1246950fe32dcc049799f9085287f0a747a6ef7a023df46a23a22f3e833bbf8d404f84344870492658256ee1dfc40fda33bb8d48fc72d4520ba9fc820c9123104a045206809037709f2a5f6723fa77d6bac5a573823d4ec3a7f1cb786a52ee2697e622e5d75962fa554d1024a6c355e21f33a63b2b72e6c4742a8b1c373aa532b40518c38c90b5373c2eb8c9d7be2a9e16047a3ee09dc9a6849deac5183ace6cfe91a9bef2ffc0a7df6ccebfd4c858c84b0e0355650d7466971e66f1e3883013e5ad1be33199b1d110b79070ac1b745ccb14cf63a08f8cca3a21c9525e626ff5f0c34746e10750fb742ad51f11f2acae3676c2111853d7250d01b77821a6ba9e04400ba2c543ca9f2d701ae6f47bfad14ffe3039ee9e71f7b2401359ade9938750ddb9c5a8b018a7929ed8d0e717ff1861446ce17535e9b17c187711190aae3388bd9490837a636c25ed4d42d7079ad1a51e13292c683d5d012abcf46965c534b83ab53f2c1f0cf5830ef7582e06863a33c19a70511df632885d63245965047ea96b56f1af5b3b94a54999f784fb9574fdfcd7c1230e07a2aaa04acd3097b2b9f8ddba05ae9734491deb5c1a513c76ed276cb78bbf4839dae3156d76af444a5805129d5df791167a9c8576a1d7f760b2d2797c4658669608706fbd0ace1be2346f74862dfc9ef518e55632e43c043186e5d070deb34d12fb9e5aba84e5cb50213dc88efd39cc35bf42455aa82d5e3b707b3140be3b8623b34fdd81d08615c188ae8438a13881fdf6bf32f2cb9ff5fa625561040c6b71d4b8eccc90bc3b99650d28dd1ee63773e49664e3d48c484996b290943635a6f2eb1ce9796d3fa144a3f00ef82faaa32d6a413668f7b521517cb68b2b017fcf56c79326fa5e4060e643631ca3f0a0dc0ed718798b6f46b130d437c33f64039e887324b6f5e604b1669d613923794edbf04b1b3caea54793b52b44b170173a4f25c7ecef3b71e2aad76e556b1cb9f1d637ec52ececfa950dd31dbb6a60828a3ad34c1beffe09eb4785786d63bad10a0b0f66ea88c57380f38ea85f018dbd7f538cf1ee7624095b9a01ec5edd528f281168af020609e651ff316aa1320a710134ddfca600cc72174dcdb846d2aa29916488aa1b537b66da92e61af526debef4eb38c984569eaf549ff2129449269b492d030cd74d885f6f5785881cc4804b4a8a09ba4ff7aefe9074ac7d0c4f05d51fe4cc0ff7388a772092b9d02d70e5433a5cf3e02f46a6bd6b818d59a07ce3b9fbbf8b5faba74563bcc5240930c2d406c9aaee3e3ce0429bf68ac2b0a57adb09414cff50817d2a48fb9fa624ab863cb0c31a8b8dc5eaf6fa68cc1d7c6c685c5a33edd5c8933b9e8ab628ee428d0743699b2ff17f25586c7ce959280bb0b8c5342251f0a30b53dbc7bf1ee426ac9619c3560f811f2268ee37f189794e2e4b3db3a2fb2e34b649e504fb467438abfd1082619cc4a0b30d66beb831077812e418d2e2148db10cf4d4a29101ca52ec445b8d83519dd7de85a98e0beae9ee537096d3f1a55a7a80cdfa93d25f07c9f98e8af18cde19ec1f99c5dd4588b717a5039ddb7f177717caf0d0fd45420a70dbd6d3146890d9e450d5224146db4c33b779e3c3a04b976c052bad042ac57dd38be45407808c0fb0d7e2a8819e6cd53c6739e6612996ddaa6f066552590aa0343bc1e62b298ff2514a0cef8be21956c2e942816f7a3a3a0935eaf9b37251409ce444c986c3817e82835555fe18239f3ae33469d7965c2bde9991fde556bd07af01df52bbde0c35bb4ef48e3b5d0db53f8ca4ed35b83f760f0a1bc4ed9f86e85d6039a17df373c85402ef956f01db00eb39c4b74bd0660d29ee746714d9780d738e05c6cca414ce3d7b40dda8036a9eea9ab1388805f913eb19bdd3f09d9e161eaa50231bd9caba61971f194332dd28c696a60458c1c6c2cc5da8b1192611c7c553e9e12fe48ce46bbb891be8bb118721c86222e671ddd1da8f0ccb2b68e02f2014b4925e904e88369aaf7466bd7033a60c265d45955944916ecbdb84bf1b522b01b0149c632e04c568a7eb627c5bb90ece052ebcf79166c28b30d23fe52da0a5ab5dea83ca479a3e3b7a9cfbbfea04dbe6137c19d067317c2ec427a8c75a6b06bec6dcd5d5c0edc9aa80b9003b8e17c088b2f3db327d3e42630d82d20120240c3ba56232280787da4aabbf5bc95a864029f00710e195f2a76460a0317d10b552fe1bea097e41d49756c680a41d6ac186e62169b6b6cd7776ea84618b5b752328a5bacaa10aa122ff9b2698b43efe73d852a899db644863c8c9bc8068ea86ea843fd6fe36272b91cdc5d5317083ef3fd1e5462a0b0d0604dc57b3bbfceb0fca4cd349625dd7b25166af30efe5ee6a0af953a74d65f4736c59918ee55a3b0d9d9d42e04c7f8a77e479109f740e20c464d5d7e3d16805f47b61f403ff7f408c9e850d9baacd8067e544536a4953480b0f9ee9cd45f41ebd67b51f78788a6470cb1e5ca72ca346ce8a50d0ca0c921d5576a4455a1afb6d0bc688004712ee122cacdb29c51e84893324c27fa4a3f1917edf5352272b4c97579a6152e4b77663d0ab532915f2eeb6a862de8b696452321b660c3f2449673d086e95a7af28845a5259b763e0fcd09f72acf7b6c811066263060e5aa5b24658e880a01fd56bda4dad5ab604e129290f7d5489728f2a40968c6168b21cebbbcd11727cc9e9160c4e92e04387d3b0d62aab06a61f26daedd9fed11816ef2180172a47f47184ac4032b88758c98a2e0fb200f70e93ba695f5ebb7a1029610ad360d3b7fa1b4640b9dc674d3625eef786da93dff19bc7991b5d6193a3896664763fde479b5dfc04812111a80782854f2cf68ca7d82765cc9eb40fba4b44640710ed6e653abf9f07b466333f4fd22784d53cf40e17120f42caa841eaa24056b237827b0f47f7257c103c35027e9f503e5acfd023e7357b600d3084d361d5ee65ba319b45c153212a54e6fed85af7e43e0a926ebcbc2edf8de7e2ec9528f00bec262ad04d5c9dafccaea06a24748d28bf1799bae0e895543084539c50b5aaa4fb50d7431d6f0c8cee2a54aaf7ee7919b55bf40adb688632e5dbe273cea09e97b19c3d8e1f4de000deb66fa1942ad03a62d3252f51992244366c156000b49c297167a6cbdedea7ebae139d295f0ad298e0864249b905b7eb812886ec70ecdb286702274b5b8574149bf3866f9e46b997ff5ed622b169a0eb071347f18d530db1663906a28f4544ee4e004ab87b65476af30ede118052ff052b8dc986ca2c93dd5d4943266a579c7698ea014f688b3e8063a107feb162d392e2177b01bff77fb5abe5feebd0607158049a5a093325b7c9ee6b4dfa7a9f65c7c2fb628920d3603a1c2dad979eaa047cd661a268af1078c9788d720e64e4ce9d12e68de1e417ef2f293323681e1071f9220e1ee43d2e29d111b870ce3439f5100ecd4551ab65ee74aa1667e564957e9bc0ae1ea193980da2a0ec2698073388c85bec25ef447f0d5e93a5203fa44dff268e5cb799ed3b66e63d5e07b487e7534f24934c73a62a243e0151843a0fd3807711a101eaa7fc71f0ba68aebb9534d57cba41b094eebfb4c31cca8eddfa426f676aa347be8a7023a4e91ddb154b35cd4d5f7dbc2e5db491de99f33fc2cff2d57029ac950e1ccd681980af6a4e8969dfe39b3c7bfcbcf8fac92f1e6ec9fe572bfa6a7d65860eab2ed10ac01a71290b52e3148e84b7376a8605cd2bb0e8681ffc54691ce087685e33921bd44d36c78291713dce17569570f62137e6904f0d68cf53aa2ec395c389a75141f08114fb293ea63950e4ffee55ec6fc83cf44876b8e7f25cdd393ff87b9eda6eb746085b61a6900de191f0ce2cb388d61ece52e78bc47368194e8e00277e0d1631e6b9d4626ef76f8522582ccd5a40be3febc699bb510acc6271d55ff0f4cf3bb7669855a72efd9ca3e1056a2fe592a5bc877cce2b1f63b58383971da87873d2d1349cf5881242cdce4e7e2c5c514755746a0e0a7c2a6d9701cde005ae3420beb17c379a3516662253554f51f0423bb1844b0b90c54ed8177ceb0e1036a6609d836e748ca06c40ca64befadc6443ec286a0ce464678e8d11eb455f7bb305acebf6cb1f50e394a9bfeb752df1687831bac9cdd811f4f112ef6658d0f8799a866374ff96c5e2b79f30e7a74f8a2bc9ed1f88f01f30e30cb78ffb2bff10108f35e910ee3be4463e9e6f0ed910e8d598326e71dfa2277ffe5579d7fe9b6018bfe295b25219eae07b3b0270665c3fa00c3e0d180812b5cd62925585de84a7c48a9a86dba96544a251654d1966e082432dc85b6149cf21e91a46020ec32b66d28ba3b6a90c0617bc6fdd55aea819af2bcf84864ad60c28fe3c9f8339d0aee68b39d97f63b6e082835d86119cf9b9fdc8b827c847ce40aa10e1577a710132316845e825345e95bdf94d0c66ec65a6c4319fce4792313663b5f7a651a6710783e6ab71608ac6cbbf3af6911adf596ccf7c172b9bd5bceb6db379967b32b143bdd11d2ee12ddf64ecef6391e0f8570e6cddd3db95204919362b89b739fa94e7c1bfde799fd5e22aa25ca6ca42e30c08e23aae2385d99ebab441072a880dcefdab74a4c9bd39d363f6d1933d59400fca161d432aa00f23b1b1c19a154be8989699d549b66d44e39896f5523443bc6ddf4a65e91f1f3fb7b52318869a05856a4fc92f3694c81ed833c972fb918f7e5\",\"cipherparams\":{\"iv\":\"8c46d6162cd4c765759aedcbce2a5874\"},\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"n\":262144,\"p\":1,\"r\":8,\"salt\":\"82fb6cdc6917609135277badacf15baa31899d08b71a5a0fa33167167c161537\"},\"mac\":\"9187b17f7eca48e6b8c586b0cd790dbe0feb876ac8385f93faa7d5e22a3c8fc7\"},\"id\":\"92caf6ee-2d43-48c0-859e-ffa1e0e23312\",\"version\":3}",
1070
+ )};
1071
+ const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
1072
+
1073
+ async function main() {
1074
+ const rpcUrl = process.env.QC_RPC_URL;
1075
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
1076
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1077
+
1078
+ await Initialize(null);
1079
+ const provider = new JsonRpcProvider(rpcUrl, chainId);
1080
+
1081
+ // Offline wallet (no provider attached). We'll resolve nonce from provider manually.
1082
+ const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE);
1083
+ const from = wallet.address;
1084
+
1085
+ const factory = new ${a.contractName}__factory(wallet);
1086
+ const deployTxReq = factory.getDeployTransaction(${ctorArgsExpr});
1087
+
1088
+ let gasLimit = 600000;
1089
+ try {
1090
+ const est = await provider.estimateGas({ from, data: deployTxReq.data });
1091
+ gasLimit = Number(est + 200_000n);
1092
+ } catch {
1093
+ gasLimit = 6_000_000;
1094
+ }
1095
+ const bytecodeSize = (${a.contractName}.bytecode || "").length;
1096
+ if (bytecodeSize > 20000 && gasLimit < 6_000_000) gasLimit = 6_000_000;
1097
+
1098
+ const nonce0 = await provider.getTransactionCount(from, "pending");
1099
+ const predicted = getCreateAddress({ from, nonce: nonce0 });
1100
+
1101
+ const rawDeploy = await wallet.signTransaction({
1102
+ ...deployTxReq,
1103
+ nonce: nonce0,
1104
+ chainId,
1105
+ gasLimit,
1106
+ gasPrice: 1n,
1107
+ });
1108
+
1109
+ const sentDeploy = await provider.sendRawTransaction(rawDeploy);
1110
+ console.log("deploy tx hash:", sentDeploy.hash);
1111
+ await sentDeploy.wait(1, 600_000);
1112
+
1113
+ const contract = ${a.contractName}.connect(predicted, provider);
1114
+ console.log("deployed at:", contract.target);
1115
+
1116
+ // Optional: offline-sign a write method if present (ERC20-like approve)
1117
+ if (contract.populateTransaction && typeof contract.populateTransaction.approve === "function") {
1118
+ const txReq = await contract.populateTransaction.approve(from, 123, { gasLimit: 200000 });
1119
+ const nonce1 = await provider.getTransactionCount(from, "pending");
1120
+ const raw = await wallet.signTransaction({ ...txReq, nonce: nonce1, chainId, gasPrice: 1n });
1121
+ const sent = await provider.sendRawTransaction(raw);
1122
+ console.log("approve tx hash:", sent.hash);
1123
+ await sent.wait(1, 600_000);
1124
+ console.log("approve succeeded");
1125
+ } else {
1126
+ console.log("No known write method for offline-signing demo (skipping write tx).");
1127
+ }
1128
+ }
1129
+
1130
+ main().catch((e) => {
1131
+ console.error(e);
1132
+ process.exitCode = 1;
1133
+ });
1134
+ `,
1135
+ );
1136
+
1137
+ _writeText(
1138
+ path.join(outDir, "examples", "events.js"),
1139
+ `/**
1140
+ * Events/logs example (generated).
1141
+ *
1142
+ * Requires:
1143
+ * - QC_RPC_URL env var
1144
+ * - CONTRACT_ADDRESS env var
1145
+ */
1146
+ const { Initialize } = require("quantumcoin/config");
1147
+ const { JsonRpcProvider } = require("quantumcoin");
1148
+ const { ${a.contractName} } = require("..");
1149
+
1150
+ async function main() {
1151
+ const rpcUrl = process.env.QC_RPC_URL;
1152
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
1153
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1154
+ const address = process.env.CONTRACT_ADDRESS;
1155
+ if (!address) throw new Error("CONTRACT_ADDRESS is required");
1156
+ await Initialize(null);
1157
+
1158
+ const provider = new JsonRpcProvider(rpcUrl, chainId);
1159
+ const contract = ${a.contractName}.connect(address, provider);
1160
+
1161
+ const fromBlock = process.env.FROM_BLOCK ? Number(process.env.FROM_BLOCK) : "latest";
1162
+ const toBlock = process.env.TO_BLOCK ? Number(process.env.TO_BLOCK) : "latest";
1163
+
1164
+ const logs = await contract.queryFilter("Transfer", fromBlock, toBlock);
1165
+ console.log("Logs:", logs.length);
1166
+ for (const l of logs.slice(0, 10)) {
1167
+ console.log(l);
1168
+ }
1169
+ }
1170
+
1171
+ main().catch((e) => {
1172
+ console.error(e);
1173
+ process.exitCode = 1;
1174
+ });
1175
+ `,
745
1176
  );
746
1177
  } else {
747
1178
  // Multi-contract: avoid filename collisions.
748
1179
  for (const a of artifacts) {
749
1180
  const ctor = _findConstructor(a.abi);
750
- const ctorArgsExpr = (ctor.inputs || []).map((i) => _solTypeToExampleValueExpr(i.type)).join(", ");
1181
+ const ctorArgsExpr = (ctor.inputs || []).map((i) => _solTypeToExampleValueExpr(i)).join(", ");
1182
+
1183
+ _writeText(
1184
+ path.join(outDir, "examples", `deploy-${a.contractName}.js`),
1185
+ `const { Initialize } = require("quantumcoin/config");\nconst { JsonRpcProvider } = require("quantumcoin");\nconst { createTestWallet } = require("./_test-wallet");\nconst { ${a.contractName}__factory } = require("..");\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL;\n if (!rpcUrl) throw new Error("QC_RPC_URL is required");\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const wallet = createTestWallet(provider);\n\n const factory = new ${a.contractName}__factory(wallet);\n const contract = await factory.deploy(${ctorArgsExpr}${ctorArgsExpr ? ", " : ""}{ gasLimit: 600000 });\n const tx = contract.deployTransaction();\n if (tx) await tx.wait(1, 600_000);\n\n console.log("Deployed ${a.contractName} at:", contract.target);\n}\n\nmain().catch((e) => { console.error(e); process.exitCode = 1; });\n`,
1186
+ );
751
1187
 
752
1188
  _writeText(
753
- path.join(outDir, "examples", `deploy-${a.contractName}.ts`),
754
- `import { Initialize } from "quantumcoin/config";\nimport { JsonRpcProvider, Wallet, encodeBytes32String } from "quantumcoin";\nimport { ${a.contractName}__factory } from "../src";\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL || "http://127.0.0.1:8545";\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const wallet = Wallet.createRandom().connect(provider);\n\n const factory = new ${a.contractName}__factory(wallet);\n const contract = await factory.deploy(${ctorArgsExpr}${ctorArgsExpr ? ", " : ""}{ gasLimit: 600000 });\n const tx = contract.deployTransaction();\n if (tx) await tx.wait();\n\n console.log("Deployed ${a.contractName} at:", contract.target);\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exitCode = 1;\n});\n`,
1189
+ path.join(outDir, "examples", `read-operations-${a.contractName}.js`),
1190
+ `const { Initialize } = require("quantumcoin/config");\nconst { JsonRpcProvider } = require("quantumcoin");\nconst { ${a.contractName} } = require("..");\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL;\n if (!rpcUrl) throw new Error("QC_RPC_URL is required");\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n const address = process.env.CONTRACT_ADDRESS;\n if (!address) throw new Error("CONTRACT_ADDRESS is required");\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const contract = ${a.contractName}.connect(address, provider);\n\n console.log("${a.contractName}:", contract.target);\n}\n\nmain().catch((e) => { console.error(e); process.exitCode = 1; });\n`,
755
1191
  );
756
1192
 
757
1193
  _writeText(
758
- path.join(outDir, "examples", `read-operations-${a.contractName}.ts`),
759
- `import { Initialize } from "quantumcoin/config";\nimport { JsonRpcProvider } from "quantumcoin";\nimport { ${a.contractName} } from "../src";\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL || "http://127.0.0.1:8545";\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n const address = process.env.CONTRACT_ADDRESS || "0x...";\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const contract = ${a.contractName}.connect(address, provider);\n\n console.log("${a.contractName}:", contract.target);\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exitCode = 1;\n});\n`,
1194
+ path.join(outDir, "examples", `write-operations-${a.contractName}.js`),
1195
+ `const { Initialize } = require("quantumcoin/config");\nconst { JsonRpcProvider } = require("quantumcoin");\nconst { createTestWallet } = require("./_test-wallet");\nconst { ${a.contractName} } = require("..");\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL;\n if (!rpcUrl) throw new Error("QC_RPC_URL is required");\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n const address = process.env.CONTRACT_ADDRESS;\n if (!address) throw new Error("CONTRACT_ADDRESS is required");\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const wallet = createTestWallet(provider);\n const contract = ${a.contractName}.connect(address, wallet);\n\n console.log("Connected:", contract.target);\n console.log("Done");\n}\n\nmain().catch((e) => { console.error(e); process.exitCode = 1; });\n`,
760
1196
  );
761
1197
 
762
1198
  _writeText(
763
- path.join(outDir, "examples", `write-operations-${a.contractName}.ts`),
764
- `import { Initialize } from "quantumcoin/config";\nimport { JsonRpcProvider, Wallet } from "quantumcoin";\nimport { ${a.contractName} } from "../src";\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL || "http://127.0.0.1:8545";\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n const address = process.env.CONTRACT_ADDRESS || "0x...";\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const wallet = Wallet.createRandom().connect(provider);\n const contract = ${a.contractName}.connect(address, wallet);\n\n // const tx = await contract.someWriteMethod(/* args */);\n // await tx.wait();\n console.log("Done");\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exitCode = 1;\n});\n`,
1199
+ path.join(outDir, "examples", `offline-signing-${a.contractName}.js`),
1200
+ `const { Initialize } = require("quantumcoin/config");\nconst { JsonRpcProvider, Wallet, getCreateAddress } = require("quantumcoin");\nconst { TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE } = require("./_test-wallet");\nconst { ${a.contractName}__factory, ${a.contractName} } = require("..");\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL;\n if (!rpcUrl) throw new Error("QC_RPC_URL is required");\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE);\n const from = wallet.address;\n\n const factory = new ${a.contractName}__factory(wallet);\n const deployTxReq = factory.getDeployTransaction();\n const nonce0 = await provider.getTransactionCount(from, \"pending\");\n const predicted = getCreateAddress({ from, nonce: nonce0 });\n\n const rawDeploy = await wallet.signTransaction({ ...deployTxReq, nonce: nonce0, chainId, gasLimit: 6_000_000, gasPrice: 1n });\n const sentDeploy = await provider.sendRawTransaction(rawDeploy);\n await sentDeploy.wait(1, 600_000);\n\n const contract = ${a.contractName}.connect(predicted, provider);\n console.log(\"deployed at:\", contract.target);\n}\n\nmain().catch((e) => { console.error(e); process.exitCode = 1; });\n`,
765
1201
  );
766
1202
 
767
1203
  _writeText(
768
- path.join(outDir, "examples", `events-${a.contractName}.ts`),
769
- `import { Initialize } from "quantumcoin/config";\nimport { JsonRpcProvider } from "quantumcoin";\nimport { ${a.contractName} } from "../src";\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL || "http://127.0.0.1:8545";\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n const address = process.env.CONTRACT_ADDRESS || "0x...";\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const contract = ${a.contractName}.connect(address, provider);\n\n console.log("${a.contractName}:", contract.target);\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exitCode = 1;\n});\n`,
1204
+ path.join(outDir, "examples", `events-${a.contractName}.js`),
1205
+ `const { Initialize } = require("quantumcoin/config");\nconst { JsonRpcProvider } = require("quantumcoin");\nconst { ${a.contractName} } = require("..");\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL;\n if (!rpcUrl) throw new Error("QC_RPC_URL is required");\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n const address = process.env.CONTRACT_ADDRESS;\n if (!address) throw new Error("CONTRACT_ADDRESS is required");\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const contract = ${a.contractName}.connect(address, provider);\n\n const logs = await contract.queryFilter("Transfer", "latest", "latest");\n console.log("Logs:", logs.length);\n}\n\nmain().catch((e) => { console.error(e); process.exitCode = 1; });\n`,
770
1206
  );
771
1207
  }
772
1208
  }
773
1209
 
774
- // Write an index.js for jsdoc-to-markdown, then install and run build scripts.
775
- _writeText(path.join(outDir, "index.js"), _renderPackageIndexJs({ artifacts }));
1210
+ // Write an index.js re-export shim, then install and run build scripts.
1211
+ _writeText(path.join(outDir, "index.js"), _renderPackageIndexJs({ artifacts, entryDir: lang === "ts" ? "dist" : "src" }));
776
1212
  }
777
1213
 
778
- // Final step: after package creation, build docs/README.
1214
+ // Final step: after package creation, emit types.
779
1215
  if (createPackage) {
780
1216
  _runNpm(["install", "--no-fund", "--no-audit"], outDir);
781
- _runNpm(["run", "build:ts"], outDir);
782
- if (process.platform === "win32") {
783
- _runNpm(["run", "build-powershell"], outDir);
784
- } else {
785
- _runNpm(["run", "build"], outDir);
1217
+ if (lang === "ts") {
1218
+ _runNpm(["run", "build:ts"], outDir);
786
1219
  }
787
1220
  }
788
1221
 
1222
+ console.warn("This is an experimental SDK. Use at your own risk.");
789
1223
  console.log(`Generated contract files in: ${targetSrcDir}`);
790
1224
  }
791
1225
 
@@ -793,26 +1227,173 @@ function _cap(s) {
793
1227
  return s ? s[0].toUpperCase() + s.slice(1) : s;
794
1228
  }
795
1229
 
796
- function _packageReadme({ pkgName, pkgDesc, contractNames }) {
797
- const list = (contractNames || []).map((n) => `- \`${n}\``).join("\n");
798
- return `# ${pkgName}\n\n${pkgDesc}\n\n## Contracts\n\n${list || "- (none)"}\n\n## Running\n\n- \`npm install\`\n- \`npm test\`\n\nTransactional tests require:\n- \`QC_RPC_URL\`\n- \`QC_CHAIN_ID\` (optional)\n`;
1230
+ function _abiParamSig(p, { includeName = true } = {}) {
1231
+ if (!p || typeof p !== "object") return "";
1232
+ const t = typeof p.type === "string" ? p.type : "";
1233
+ const n = includeName && typeof p.name === "string" && p.name ? ` ${p.name}` : "";
1234
+ const indexed = p.indexed ? " indexed" : "";
1235
+ return `${t}${indexed}${n}`.trim();
1236
+ }
1237
+
1238
+ function _abiFnSig(f) {
1239
+ const inputs = (f.inputs || []).map((p) => _abiParamSig(p)).filter(Boolean).join(", ");
1240
+ const outputs = (f.outputs || []).map((p) => _abiParamSig(p, { includeName: false })).filter(Boolean).join(", ");
1241
+ const mut = f.stateMutability && f.stateMutability !== "nonpayable" ? ` ${f.stateMutability}` : "";
1242
+ const returns = outputs ? ` returns (${outputs})` : "";
1243
+ return `${f.name}(${inputs})${mut}${returns}`.trim();
1244
+ }
1245
+
1246
+ function _abiEventSig(e) {
1247
+ const inputs = (e.inputs || []).map((p) => _abiParamSig(p)).filter(Boolean).join(", ");
1248
+ return `${e.name}(${inputs})`.trim();
1249
+ }
1250
+
1251
+ function _abiErrorSig(er) {
1252
+ const inputs = (er.inputs || []).map((p) => _abiParamSig(p)).filter(Boolean).join(", ");
1253
+ return `${er.name}(${inputs})`.trim();
1254
+ }
1255
+
1256
+ function _firstLine(s) {
1257
+ if (!s) return "";
1258
+ const t = String(s).trim();
1259
+ if (!t) return "";
1260
+ return t.split(/\r?\n/g)[0].trim();
1261
+ }
1262
+
1263
+ function _packageReadme({ pkgName, pkgDesc, artifacts, createdFromSolidity, lang = "ts" }) {
1264
+ const outLang = _normalizeLang(lang);
1265
+ const srcExt = outLang === "js" ? "js" : "ts";
1266
+ const list = (artifacts || []).map((a) => a.contractName).filter(Boolean);
1267
+ const hasMultiple = list.length > 1;
1268
+
1269
+ const contractLinks = list.length
1270
+ ? list.map((n) => `- [\`${n}\`](#${n.toLowerCase()})`).join("\n")
1271
+ : "- (none)";
1272
+
1273
+ const envBlock = `- \`QC_RPC_URL\` (required for transactional tests)\n- \`QC_CHAIN_ID\` (optional; defaults are used if omitted)\n`;
1274
+
1275
+ const commonExamples = hasMultiple
1276
+ ? `Examples are generated per contract (e.g. \`examples/deploy-<Contract>.js\`).`
1277
+ : `- [deploy](./examples/deploy.js)\n- [read operations](./examples/read-operations.js)\n- [write operations](./examples/write-operations.js)\n- [events](./examples/events.js)\n`;
1278
+
1279
+ const contractsMd = (artifacts || [])
1280
+ .map((a) => {
1281
+ const name = a.contractName;
1282
+ const desc = a.docs && typeof a.docs.contract === "string" && a.docs.contract.trim() ? a.docs.contract.trim() : "";
1283
+
1284
+ const fnDocs = (a.docs && a.docs.functions) || {};
1285
+ const abi = Array.isArray(a.abi) ? a.abi : [];
1286
+ const functions = abi.filter((x) => x && x.type === "function").sort((x, y) => String(x.name).localeCompare(String(y.name)));
1287
+ const events = abi.filter((x) => x && x.type === "event").sort((x, y) => String(x.name).localeCompare(String(y.name)));
1288
+ const errors = abi.filter((x) => x && x.type === "error").sort((x, y) => String(x.name).localeCompare(String(y.name)));
1289
+ const ctor = abi.find((x) => x && x.type === "constructor");
1290
+
1291
+ const examples = hasMultiple
1292
+ ? `- [deploy](./examples/deploy-${name}.js)\n- [read operations](./examples/read-operations-${name}.js)\n- [write operations](./examples/write-operations-${name}.js)\n- [events](./examples/events-${name}.js)\n`
1293
+ : `- [deploy](./examples/deploy.js)\n- [read operations](./examples/read-operations.js)\n- [write operations](./examples/write-operations.js)\n- [events](./examples/events.js)\n`;
1294
+
1295
+ const testLink = `- [transactional test](./test/e2e/${name}.e2e.test.js)\n`;
1296
+
1297
+ const fileLinks = [
1298
+ `- [\`src/${name}.${srcExt}\`](./src/${name}.${srcExt})`,
1299
+ `- [\`src/${name}__factory.${srcExt}\`](./src/${name}__factory.${srcExt})`,
1300
+ createdFromSolidity ? `- [\`artifacts/${name}.abi.json\`](./artifacts/${name}.abi.json)` : null,
1301
+ createdFromSolidity ? `- [\`artifacts/${name}.bin\`](./artifacts/${name}.bin)` : null,
1302
+ ]
1303
+ .filter(Boolean)
1304
+ .join("\n");
1305
+
1306
+ const ctorSig = ctor
1307
+ ? `\`constructor(${(ctor.inputs || []).map((p) => _abiParamSig(p)).filter(Boolean).join(", ")})\``
1308
+ : "`constructor()`";
1309
+
1310
+ const fnList = functions.length
1311
+ ? functions
1312
+ .map((f) => {
1313
+ const doc = fnDocs && typeof fnDocs[f.name] === "string" ? _firstLine(fnDocs[f.name]) : "";
1314
+ return `- \`${_abiFnSig(f)}\`${doc ? ` — ${doc}` : ""}`;
1315
+ })
1316
+ .join("\n")
1317
+ : "- (none)";
1318
+
1319
+ const eventList = events.length ? events.map((e) => `- \`${_abiEventSig(e)}\``).join("\n") : "- (none)";
1320
+ const errorList = errors.length ? errors.map((er) => `- \`${_abiErrorSig(er)}\``).join("\n") : "- (none)";
1321
+
1322
+ return [
1323
+ `## ${name}`,
1324
+ desc ? `\n${desc}\n` : "",
1325
+ `- **Exports**: \`${name}\`, \`${name}__factory\``,
1326
+ `- **Constructor**: ${ctorSig}`,
1327
+ "",
1328
+ "### Files",
1329
+ fileLinks,
1330
+ "",
1331
+ "### Examples",
1332
+ examples.trimEnd(),
1333
+ "",
1334
+ "### Tests",
1335
+ testLink.trimEnd(),
1336
+ "",
1337
+ "### Functions",
1338
+ fnList,
1339
+ "",
1340
+ "### Events",
1341
+ eventList,
1342
+ "",
1343
+ "### Errors",
1344
+ errorList,
1345
+ "",
1346
+ ]
1347
+ .filter(Boolean)
1348
+ .join("\n");
1349
+ })
1350
+ .join("\n");
1351
+
1352
+ return (
1353
+ `# ${pkgName}\n\n` +
1354
+ `${pkgDesc || ""}\n\n` +
1355
+ "> **Note:** This is an experimental SDK. Use at your own risk.\n\n" +
1356
+ "## What’s in this package\n\n" +
1357
+ (outLang === "ts"
1358
+ ? "- Typed contract wrappers and factories in `src/` (compiled output in `dist/`)\n"
1359
+ : "- JavaScript contract wrappers and factories in `src/` (TypeScript types via `.d.ts`)\n") +
1360
+ "- Transactional tests in `test/e2e/`\n" +
1361
+ `- Example scripts in \`examples/\`\n` +
1362
+ (createdFromSolidity ? "- ABI/BIN artifacts in `artifacts/`\n" : "") +
1363
+ "\n" +
1364
+ "## Install\n\n" +
1365
+ "- `npm install`\n\n" +
1366
+ "## Build\n\n" +
1367
+ (outLang === "ts" ? "- `npm run build:ts`\n\n" : "- (no build step required)\n\n") +
1368
+ "## Run tests\n\n" +
1369
+ "- `npm test`\n\n" +
1370
+ "Transactional tests require:\n" +
1371
+ envBlock +
1372
+ "\n" +
1373
+ "## Examples\n\n" +
1374
+ (hasMultiple ? `${commonExamples}\n\n` : `${commonExamples}\n`) +
1375
+ "## Contracts\n\n" +
1376
+ contractLinks +
1377
+ "\n\n" +
1378
+ (contractsMd ? contractsMd : "") +
1379
+ "\n"
1380
+ );
799
1381
  }
800
1382
 
801
- function _createPackageScaffold({ outDir, pkgName, pkgDesc, pkgAuthor, pkgLicense, pkgVersion }) {
1383
+ function _createPackageScaffold({ outDir, pkgName, pkgDesc, pkgAuthor, pkgLicense, pkgVersion, lang = "ts" }) {
802
1384
  _ensureDir(outDir);
803
1385
  _ensureDir(path.join(outDir, "src"));
804
1386
  _ensureDir(path.join(outDir, "test", "e2e"));
805
1387
  _ensureDir(path.join(outDir, "examples"));
806
1388
 
1389
+ const outLang = _normalizeLang(lang);
1390
+ const isTs = outLang === "ts";
1391
+
807
1392
  const rootPkg = _readRootPackageJson();
808
1393
  const rootDeps = _rewriteFileDepsToAbsolute(rootPkg.dependencies || {}, __dirname);
809
1394
 
810
1395
  // Ensure the generated package depends on this repo's quantumcoin via absolute file path.
811
1396
  rootDeps.quantumcoin = `file:${__dirname.replace(/\\\\/g, "/")}`;
812
- // Ensure docs tool is present (required by build scripts below).
813
- if (!rootDeps["jsdoc-to-markdown"]) {
814
- rootDeps["jsdoc-to-markdown"] = "latest";
815
- }
816
1397
 
817
1398
  const pkgJson = {
818
1399
  name: pkgName,
@@ -820,37 +1401,56 @@ function _createPackageScaffold({ outDir, pkgName, pkgDesc, pkgAuthor, pkgLicens
820
1401
  description: pkgDesc,
821
1402
  author: pkgAuthor,
822
1403
  license: pkgLicense,
823
- main: "dist/index.js",
824
- types: "dist/index.d.ts",
1404
+ main: isTs ? "dist/index.js" : "src/index.js",
1405
+ types: isTs ? "dist/index.d.ts" : "src/index.d.ts",
825
1406
  scripts: {
826
- "build:ts": "npx -p typescript tsc -p tsconfig.json",
827
- "build": "npx -p typescript tsc index.js --declaration --allowJs --emitDeclarationOnly && jsdoc2md index.js >README.md",
828
- "build-powershell": "powershell npx -p typescript tsc index.js --declaration --allowJs --emitDeclarationOnly; jsdoc2md index.js >README.md",
829
- test: "npm run build:ts && node --test --test-concurrency=1 \"test/**/*.test.js\"",
830
- "test:e2e": "npm run build:ts && node --test --test-concurrency=1 \"test/e2e/**/*.test.js\"",
1407
+ ...(isTs
1408
+ ? {
1409
+ "build:ts": "npx -p typescript tsc -p tsconfig.json",
1410
+ build: "npm run build:ts",
1411
+ "build-powershell": "npm run build:ts",
1412
+ test: "npm run build:ts && node --test --test-concurrency=1 \"test/**/*.test.js\"",
1413
+ "test:e2e": "npm run build:ts && node --test --test-concurrency=1 \"test/e2e/**/*.test.js\"",
1414
+ }
1415
+ : {
1416
+ build: "node -e \"console.log('JS package: no build step required')\"",
1417
+ "build-powershell": "node -e \"console.log('JS package: no build step required')\"",
1418
+ test: "node --test --test-concurrency=1 \"test/**/*.test.js\"",
1419
+ "test:e2e": "node --test --test-concurrency=1 \"test/e2e/**/*.test.js\"",
1420
+ }),
831
1421
  },
832
1422
  dependencies: rootDeps,
833
1423
  devDependencies: {},
834
1424
  };
835
1425
 
836
1426
  _writeJson(path.join(outDir, "package.json"), pkgJson);
837
- _writeJson(path.join(outDir, "tsconfig.json"), {
838
- compilerOptions: {
839
- target: "ES2022",
840
- module: "CommonJS",
841
- declaration: true,
842
- outDir: "dist",
843
- strict: true,
844
- esModuleInterop: true,
845
- skipLibCheck: true,
846
- },
847
- include: ["src/**/*.ts"],
848
- });
1427
+ if (isTs) {
1428
+ _writeJson(path.join(outDir, "tsconfig.json"), {
1429
+ compilerOptions: {
1430
+ target: "ES2022",
1431
+ lib: ["ES2022"],
1432
+ module: "CommonJS",
1433
+ declaration: true,
1434
+ outDir: "dist",
1435
+ strict: true,
1436
+ esModuleInterop: true,
1437
+ skipLibCheck: true,
1438
+ },
1439
+ include: ["src/**/*.ts"],
1440
+ });
1441
+ }
849
1442
 
850
- _writeText(path.join(outDir, "README.md"), _packageReadme({ pkgName, pkgDesc, contractNames: [] }));
1443
+ _writeText(
1444
+ path.join(outDir, "README.md"),
1445
+ _packageReadme({ pkgName, pkgDesc, artifacts: [], createdFromSolidity: false, lang: outLang }),
1446
+ );
851
1447
 
852
1448
  _writeText(path.join(outDir, ".gitignore"), `node_modules\n/dist\n*.log\n`);
853
1449
 
1450
+ // Provide a root index.d.ts without needing a separate build step.
1451
+ // This is mainly for convenience and for tooling that expects a top-level .d.ts.
1452
+ _writeText(path.join(outDir, "index.d.ts"), `export * from "./${isTs ? "dist" : "src"}";\n`);
1453
+
854
1454
  // Minimal shims so the generated TypeScript can compile even though `quantumcoin`
855
1455
  // is a JavaScript package (no bundled .d.ts in this repo).
856
1456
  _writeText(
@@ -865,6 +1465,8 @@ function _createPackageScaffold({ outDir, pkgName, pkgDesc, pkgAuthor, pkgLicens
865
1465
  ` constructor(address: string, abi: any, runner?: any, bytecode?: any);\n` +
866
1466
  ` target: string;\n` +
867
1467
  ` address: string;\n` +
1468
+ ` interface: any;\n` +
1469
+ ` populateTransaction: any;\n` +
868
1470
  ` call(methodName: string, args: any[], overrides?: TransactionRequest): Promise<any>;\n` +
869
1471
  ` send(methodName: string, args: any[], overrides?: TransactionRequest): Promise<ContractTransactionResponse>;\n` +
870
1472
  ` deployTransaction(): TransactionResponse | null;\n` +