quantumcoin 7.0.3 → 7.0.5

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 (155) hide show
  1. package/.github/workflows/publish-npmjs.yaml +22 -22
  2. package/.gitignore +15 -15
  3. package/LICENSE +21 -21
  4. package/README-SDK.md +758 -754
  5. package/README.md +165 -150
  6. package/SPEC.md +3845 -3843
  7. package/config.d.ts +50 -50
  8. package/config.js +115 -115
  9. package/examples/AllSolidityTypes.sol +184 -184
  10. package/examples/SimpleIERC20.sol +74 -74
  11. package/examples/events.js +41 -35
  12. package/examples/events.ts +35 -0
  13. package/examples/example-generator-sdk-js.js +100 -95
  14. package/examples/example-generator-sdk-js.ts +77 -0
  15. package/examples/example-generator-sdk-ts.js +100 -95
  16. package/examples/example-generator-sdk-ts.ts +77 -0
  17. package/examples/example.js +72 -61
  18. package/examples/example.ts +61 -0
  19. package/examples/offline-signing.js +79 -73
  20. package/examples/offline-signing.ts +66 -0
  21. package/examples/package-lock.json +596 -57
  22. package/examples/package.json +32 -16
  23. package/examples/read-operations.js +32 -27
  24. package/examples/read-operations.ts +31 -0
  25. package/examples/sdk-generator-erc20.inline.json +251 -251
  26. package/examples/solidity-types.ts +43 -43
  27. package/examples/wallet-offline.js +35 -29
  28. package/examples/wallet-offline.ts +34 -0
  29. package/generate-sdk.js +1824 -1490
  30. package/index.js +12 -12
  31. package/package.json +95 -75
  32. package/scripts/copy-declarations.js +31 -0
  33. package/scripts/run-all-one-by-one.js +151 -0
  34. package/src/abi/fragments.d.ts +42 -42
  35. package/src/abi/fragments.js +63 -63
  36. package/src/abi/index.d.ts +13 -13
  37. package/src/abi/index.js +9 -9
  38. package/src/abi/interface.d.ts +128 -132
  39. package/src/abi/interface.js +590 -590
  40. package/src/abi/js-abi-coder.d.ts +8 -0
  41. package/src/abi/js-abi-coder.js +474 -474
  42. package/src/constants.d.ts +66 -61
  43. package/src/constants.js +101 -94
  44. package/src/contract/contract-factory.d.ts +28 -28
  45. package/src/contract/contract-factory.js +105 -105
  46. package/src/contract/contract.d.ts +113 -114
  47. package/src/contract/contract.js +354 -354
  48. package/src/contract/index.d.ts +9 -9
  49. package/src/contract/index.js +9 -9
  50. package/src/errors/index.d.ts +92 -92
  51. package/src/errors/index.js +188 -188
  52. package/src/generator/index.d.ts +74 -0
  53. package/src/generator/index.js +1404 -1404
  54. package/src/index.d.ts +125 -127
  55. package/src/index.js +41 -41
  56. package/src/internal/hex.d.ts +61 -61
  57. package/src/internal/hex.js +144 -144
  58. package/src/providers/extra-providers.d.ts +139 -128
  59. package/src/providers/extra-providers.js +600 -575
  60. package/src/providers/index.d.ts +17 -16
  61. package/src/providers/index.js +10 -10
  62. package/src/providers/json-rpc-provider.d.ts +12 -12
  63. package/src/providers/json-rpc-provider.js +79 -79
  64. package/src/providers/provider.d.ts +208 -203
  65. package/src/providers/provider.js +393 -371
  66. package/src/types/index.d.ts +214 -462
  67. package/src/types/index.js +9 -9
  68. package/src/utils/address.d.ts +72 -72
  69. package/src/utils/address.js +181 -182
  70. package/src/utils/encoding.d.ts +120 -120
  71. package/src/utils/encoding.js +306 -306
  72. package/src/utils/hashing.d.ts +82 -76
  73. package/src/utils/hashing.js +313 -298
  74. package/src/utils/index.d.ts +65 -55
  75. package/src/utils/index.js +13 -13
  76. package/src/utils/result.d.ts +57 -57
  77. package/src/utils/result.js +128 -128
  78. package/src/utils/rlp.d.ts +12 -12
  79. package/src/utils/rlp.js +200 -200
  80. package/src/utils/units.d.ts +29 -29
  81. package/src/utils/units.js +107 -107
  82. package/src/wallet/index.d.ts +10 -10
  83. package/src/wallet/index.js +8 -8
  84. package/src/wallet/wallet.d.ts +168 -160
  85. package/src/wallet/wallet.js +500 -489
  86. package/test/e2e/all-solidity-types.dynamic.test.js +207 -200
  87. package/test/e2e/all-solidity-types.dynamic.test.ts +191 -0
  88. package/test/e2e/all-solidity-types.fixtures.js +231 -231
  89. package/test/e2e/all-solidity-types.generated-sdks.e2e.test.js +387 -368
  90. package/test/e2e/all-solidity-types.generated-sdks.e2e.test.ts +350 -0
  91. package/test/e2e/helpers.js +59 -47
  92. package/test/e2e/signing-context-and-fee.e2e.test.js +141 -0
  93. package/test/e2e/signing-context-and-fee.e2e.test.ts +128 -0
  94. package/test/e2e/simple-erc20.generated-sdks.e2e.test.js +168 -151
  95. package/test/e2e/simple-erc20.generated-sdks.e2e.test.ts +141 -0
  96. package/test/e2e/transactional.test.js +245 -191
  97. package/test/e2e/transactional.test.ts +208 -0
  98. package/test/e2e/typed-generator.e2e.test.js +407 -404
  99. package/test/e2e/typed-generator.e2e.test.ts +337 -0
  100. package/test/fixtures/ConstructorParam.sol +23 -23
  101. package/test/fixtures/MultiContracts.sol +37 -37
  102. package/test/fixtures/SimpleStorage.sol +18 -18
  103. package/test/fixtures/StakingContract.abi.json +1 -1
  104. package/test/integration/ipc-provider.test.js +49 -44
  105. package/test/integration/ipc-provider.test.ts +44 -0
  106. package/test/integration/provider.test.js +88 -72
  107. package/test/integration/provider.test.ts +85 -0
  108. package/test/integration/ws-provider.test.js +41 -33
  109. package/test/integration/ws-provider.test.ts +38 -0
  110. package/test/security/malformed-input.test.js +37 -31
  111. package/test/security/malformed-input.test.ts +35 -0
  112. package/test/unit/_encrypted-output.txt +6 -0
  113. package/test/unit/_log-encrypted-jsons.js +45 -0
  114. package/test/unit/_write-keystore-fixture.js +16 -0
  115. package/test/unit/abi-interface.test.js +103 -98
  116. package/test/unit/abi-interface.test.ts +102 -0
  117. package/test/unit/address-wallet.test.js +392 -257
  118. package/test/unit/address-wallet.test.ts +379 -0
  119. package/test/unit/browser-provider.test.js +85 -82
  120. package/test/unit/browser-provider.test.ts +79 -0
  121. package/test/unit/contract.test.js +85 -82
  122. package/test/unit/contract.test.ts +83 -0
  123. package/test/unit/encoding-units-rlp.test.js +92 -89
  124. package/test/unit/encoding-units-rlp.test.ts +91 -0
  125. package/test/unit/errors.test.js +77 -74
  126. package/test/unit/errors.test.ts +76 -0
  127. package/test/unit/filter-by-blockhash.test.js +55 -52
  128. package/test/unit/filter-by-blockhash.test.ts +54 -0
  129. package/test/unit/fixtures/encrypted-keystores-48-32-36.js +9 -0
  130. package/test/unit/generate-contract-cli.test.js +42 -39
  131. package/test/unit/generate-contract-cli.test.ts +41 -0
  132. package/test/unit/generate-sdk-artifacts-json.test.js +113 -110
  133. package/test/unit/generate-sdk-artifacts-json.test.ts +110 -0
  134. package/test/unit/generator.test.js +102 -99
  135. package/test/unit/generator.test.ts +101 -0
  136. package/test/unit/hashing.test.js +68 -54
  137. package/test/unit/hashing.test.ts +67 -0
  138. package/test/unit/init.test.js +39 -36
  139. package/test/unit/init.test.ts +38 -0
  140. package/test/unit/interface.test.js +56 -53
  141. package/test/unit/interface.test.ts +54 -0
  142. package/test/unit/internal-hex.test.js +50 -47
  143. package/test/unit/internal-hex.test.ts +49 -0
  144. package/test/unit/populate-transaction.test.js +65 -62
  145. package/test/unit/populate-transaction.test.ts +64 -0
  146. package/test/unit/providers.test.js +200 -144
  147. package/test/unit/providers.test.ts +196 -0
  148. package/test/unit/result.test.js +80 -77
  149. package/test/unit/result.test.ts +79 -0
  150. package/test/unit/solidity-types.test.js +49 -46
  151. package/test/unit/solidity-types.test.ts +39 -0
  152. package/test/unit/utils.test.js +57 -54
  153. package/test/unit/utils.test.ts +56 -0
  154. package/test/verbose-logger.js +74 -0
  155. package/tsconfig.build.json +14 -0
package/generate-sdk.js CHANGED
@@ -1,1490 +1,1824 @@
1
- #!/usr/bin/env node
2
- /**
3
- * sdkgen
4
- *
5
- * NOTE: This script is the SDK generator CLI entrypoint.
6
- *
7
- * SPEC.md section 15: Typed Contract Generator
8
- *
9
- * Usage:
10
- * - Non-interactive:
11
- * `node generate-sdk.js --abi path/to/abi.json --bin path/to/bytecode.bin --out ./out --name MyContract`
12
- *
13
- * - Interactive:
14
- * `node generate-sdk.js --abi path/to/abi.json --bin path/to/bytecode.bin`
15
- */
16
-
17
- const fs = require("node:fs");
18
- const path = require("node:path");
19
- const readline = require("node:readline/promises");
20
- const { stdin, stdout } = require("node:process");
21
- const { execFileSync, spawnSync } = require("node:child_process");
22
-
23
- const { generate, generateFromArtifacts, generateTransactionalTestJs, generateAllContractsTransactionalTestJs } = require("./src/generator");
24
-
25
- function _helpText() {
26
- return `
27
- sdkgen (generate-sdk.js)
28
-
29
- Generates TypeScript or JavaScript contract wrappers (plus optional package scaffold, examples and transactional tests).
30
-
31
- If you run this script with no parameters, it prints this help.
32
-
33
- USAGE
34
- node generate-sdk.js [options]
35
-
36
- INPUT MODES
37
- 1) ABI + BIN (bytecode)
38
- --abi <path/to/Contract.abi.json>
39
- --bin <path/to/Contract.bin>
40
- --name <ContractName> (optional; defaults from ABI filename)
41
-
42
- 2) Solidity (.sol) sources
43
- --sol "<A.sol,B.sol,...>" (comma-separated list; can be 1+ files)
44
- --name <ContractName> (optional; if omitted, generates ALL deployable contracts found)
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")
47
-
48
- 3) Artifacts JSON (array of ABI+BIN pairs)
49
- --artifacts-json <path/to/artifacts.json>
50
- The JSON file should be an array. Each entry supports:
51
- - { abi: "<path|abiJsonString|abiArray>", bin: "<path|0x...>", name?: "<ContractName>" }
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
- ]
75
-
76
- PACKAGE OUTPUT (optional)
77
- --create-package
78
- --package-dir <folder>
79
- --package-name <name>
80
- --package-description <text>
81
- --package-author <text>
82
- --package-license <spdx> (default: MIT)
83
- --package-version <semver> (default: 0.0.1)
84
-
85
- Notes:
86
- - When --create-package is used, the generator creates a complete npm package scaffold with:
87
- - src/*.(ts|js) (contracts + factories; depends on --lang)
88
- - test/e2e/*.e2e.test.js (transactional tests)
89
- - examples/ (deploy/read/write/events)
90
- - artifacts/ (only when using --sol)
91
- - index.js + index.d.ts (package entry shims; TS mode uses dist/)
92
- - For --lang ts, the generator will run:
93
- npm install
94
- npm run build:ts
95
- as the final step.
96
- - For --lang js, the generator will run:
97
- npm install
98
- and no build step is required.
99
-
100
- GENERAL OPTIONS
101
- --out <folder> Output folder (default: ./generated-contract)
102
- --lang <ts|js> Output language for generated contract sources (default: ts)
103
- --non-interactive | --yes Disable prompts (required for CI)
104
- -h | --help Show this help
105
-
106
- ENVIRONMENT
107
- QC_SOLC_PATH / SOLC / SOLC_PATH
108
- Path to solc executable used when compiling Solidity.
109
- Default: c:\\solc\\solc.exe
110
-
111
- QC_RPC_URL / QC_CHAIN_ID
112
- Used by the auto-generated transactional tests in the generated package.
113
-
114
- EXAMPLES
115
- Generate typed files from ABI+BIN into ./out:
116
- node generate-sdk.js --abi .\\artifacts\\MyContract.abi.json --bin .\\artifacts\\MyContract.bin --out .\\out --name MyContract --non-interactive
117
-
118
- Generate a new typed package from ABI+BIN:
119
- node generate-sdk.js --abi .\\My.abi.json --bin .\\My.bin --name MyContract --create-package --package-dir .\\tmp --package-name my-typed-contract --package-description "My typed contract" --package-author "me" --non-interactive
120
-
121
- Generate a new typed package from Solidity (single file):
122
- set QC_SOLC_PATH=c:\\solc\\solc.exe
123
- node generate-sdk.js --sol ".\\contracts\\MyContract.sol" --create-package --package-dir .\\tmp --package-name my-typed-contract --package-description "My typed contract" --package-author "me" --non-interactive
124
-
125
- Generate a new typed package from Solidity (multiple files):
126
- node generate-sdk.js --sol ".\\contracts\\A.sol,.\\contracts\\B.sol,.\\contracts\\lib\\C.sol" --create-package --package-dir .\\tmp --package-name my-typed-contract --package-description "My typed contract" --package-author "me" --non-interactive
127
-
128
- `.trimStart();
129
- }
130
-
131
- function _argValue(argv, name) {
132
- const idx = argv.indexOf(name);
133
- if (idx === -1) return null;
134
- return argv[idx + 1] || null;
135
- }
136
-
137
- function _hasFlag(argv, name) {
138
- return argv.includes(name);
139
- }
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
-
149
- function _basenameNoExt(p) {
150
- const b = path.basename(p);
151
- const i = b.lastIndexOf(".");
152
- return i === -1 ? b : b.slice(0, i);
153
- }
154
-
155
- function _defaultContractNameFromAbiPath(abiPath) {
156
- const b = path.basename(abiPath);
157
- if (/\.abi\.json$/i.test(b)) return b.replace(/\.abi\.json$/i, "");
158
- return _basenameNoExt(abiPath);
159
- }
160
-
161
- function _ensureDir(p) {
162
- fs.mkdirSync(p, { recursive: true });
163
- }
164
-
165
- function _readRootDependencies() {
166
- const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8"));
167
- return pkg.dependencies || {};
168
- }
169
-
170
- function _readRootPackageJson() {
171
- return JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8"));
172
- }
173
-
174
- function _readJson(filePath) {
175
- return JSON.parse(fs.readFileSync(filePath, "utf8"));
176
- }
177
-
178
- function _looksLikeHex(s) {
179
- if (typeof s !== "string") return false;
180
- const t = s.trim();
181
- if (!t) return false;
182
- if (t.startsWith("0x")) return /^0x[0-9a-fA-F]*$/.test(t);
183
- return /^[0-9a-fA-F]*$/.test(t);
184
- }
185
-
186
- function _readArtifactsJson(fileAbs) {
187
- const baseDir = path.dirname(fileAbs);
188
- const parsed = _readJson(fileAbs);
189
- if (!Array.isArray(parsed)) throw new Error(`Artifacts JSON must be an array: ${fileAbs}`);
190
-
191
- /** @type {Array<{ contractName: string, abi: any[], bytecode: string, docs?: any }>} */
192
- const artifacts = [];
193
-
194
- for (let i = 0; i < parsed.length; i++) {
195
- const entry = parsed[i];
196
- if (!entry || typeof entry !== "object") throw new Error(`Invalid artifacts entry at index ${i} (expected object)`);
197
-
198
- const abiField = entry.abi ?? entry.abiPath;
199
- const binField = entry.bin ?? entry.bytecode ?? entry.binPath ?? entry.bytecodePath;
200
- const nameField = entry.name ?? entry.contractName;
201
-
202
- let abi;
203
- let abiPath = null;
204
- if (typeof abiField === "string") {
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
- }
221
- } else if (Array.isArray(abiField)) {
222
- abi = abiField;
223
- } else {
224
- throw new Error(`Invalid "abi" for artifacts entry ${i} (expected path, ABI JSON string, or ABI array)`);
225
- }
226
- if (!Array.isArray(abi)) throw new Error(`ABI must be an array (entry ${i})`);
227
-
228
- let bytecodeRaw;
229
- if (typeof binField === "string") {
230
- const maybePath = path.resolve(baseDir, binField);
231
- if (fs.existsSync(maybePath) && fs.statSync(maybePath).isFile()) {
232
- bytecodeRaw = fs.readFileSync(maybePath, "utf8").trim();
233
- } else if (_looksLikeHex(binField)) {
234
- bytecodeRaw = binField.trim();
235
- } else {
236
- throw new Error(`BIN/bytecode file not found (entry ${i}): ${maybePath}`);
237
- }
238
- } else {
239
- throw new Error(`Invalid "bin" for artifacts entry ${i} (expected path or hex string)`);
240
- }
241
-
242
- const bytecode = bytecodeRaw.startsWith("0x") ? bytecodeRaw : `0x${bytecodeRaw}`;
243
-
244
- let contractName = null;
245
- if (typeof nameField === "string" && nameField.trim()) {
246
- contractName = _cap(nameField.trim());
247
- } else if (abiPath) {
248
- contractName = _cap(_defaultContractNameFromAbiPath(abiPath));
249
- } else {
250
- throw new Error(`Missing contract name for artifacts entry ${i}. Provide "name" or use abi as a path.`);
251
- }
252
-
253
- artifacts.push({ contractName, abi, bytecode, docs: null });
254
- }
255
-
256
- // Ensure stable ordering and unique names
257
- artifacts.sort((a, b) => a.contractName.localeCompare(b.contractName));
258
- const seen = new Set();
259
- for (const a of artifacts) {
260
- if (seen.has(a.contractName)) throw new Error(`Duplicate contractName in artifacts JSON: ${a.contractName}`);
261
- seen.add(a.contractName);
262
- }
263
-
264
- if (artifacts.length === 0) throw new Error("Artifacts JSON contained no entries.");
265
- return artifacts;
266
- }
267
-
268
- function _rewriteFileDepsToAbsolute(deps, rootDir) {
269
- const out = { ...deps };
270
- for (const [name, ver] of Object.entries(out)) {
271
- if (typeof ver === "string" && ver.startsWith("file:")) {
272
- const rel = ver.slice("file:".length);
273
- const abs = path.resolve(rootDir, rel);
274
- out[name] = `file:${abs.replace(/\\\\/g, "/")}`;
275
- }
276
- }
277
- return out;
278
- }
279
-
280
- function _writeJson(filePath, obj) {
281
- fs.writeFileSync(filePath, JSON.stringify(obj, null, 2) + "\n", "utf8");
282
- }
283
-
284
- function _writeText(filePath, content) {
285
- fs.writeFileSync(filePath, content, "utf8");
286
- }
287
-
288
- function _findConstructor(abi) {
289
- const ctor = (abi || []).find((f) => f && f.type === "constructor");
290
- return ctor || { type: "constructor", inputs: [] };
291
- }
292
-
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)
299
- if (type.endsWith("]")) {
300
- const inner = type.slice(0, type.lastIndexOf("["));
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}]`;
311
- }
312
-
313
- if (type === "address") return "wallet.address";
314
- if (type === "bool") return "true";
315
- if (type === "string") return JSON.stringify("hello");
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
-
339
- return "undefined";
340
- }
341
-
342
- function _parseCommaSeparatedFiles(v) {
343
- if (!v) return [];
344
- return v
345
- .split(",")
346
- .map((s) => s.trim())
347
- .filter(Boolean);
348
- }
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
-
413
- function _resolveSolcPath(argv) {
414
- const solcArg = _argValue(argv, "--solc") || _argValue(argv, "--solc-path") || _argValue(argv, "--solcPath");
415
- const env =
416
- solcArg ||
417
- process.env.QC_SOLC_PATH ||
418
- process.env.SOLC ||
419
- process.env.SOLC_PATH ||
420
- process.env.solc ||
421
- null;
422
- return env || "c:\\solc\\solc.exe";
423
- }
424
-
425
- function _assertSolcExists(solcPath) {
426
- if (!fs.existsSync(solcPath)) {
427
- throw new Error(
428
- `solc not found at ${solcPath}. Set env QC_SOLC_PATH or SOLC (e.g. c:\\\\solc\\\\solc.exe) or pass --solc <path>.`,
429
- );
430
- }
431
- }
432
-
433
- function _compileSolidityToArtifacts({ solcPath, solFilesAbs, contractNameFilter, solcExtraArgs }) {
434
- _assertSolcExists(solcPath);
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" });
438
- const parsed = JSON.parse(out);
439
-
440
- const artifacts = [];
441
- for (const key of Object.keys(parsed.contracts || {})) {
442
- const entry = parsed.contracts[key];
443
- const name = key.includes(":") ? key.split(":").pop() : key;
444
- if (!name) continue;
445
- if (contractNameFilter && name !== contractNameFilter) continue;
446
- const abi = entry && entry.abi ? JSON.parse(entry.abi) : null;
447
- const bin = entry && typeof entry.bin === "string" ? entry.bin : "";
448
- if (!abi || !Array.isArray(abi)) continue;
449
- if (!bin) continue; // skip abstract/interfaces
450
- artifacts.push({ contractName: name, abi, bytecode: bin.startsWith("0x") ? bin : `0x${bin}` });
451
- }
452
-
453
- if (contractNameFilter && artifacts.length === 0) {
454
- throw new Error(`No compiled contract matched --name ${contractNameFilter}`);
455
- }
456
- if (artifacts.length === 0) {
457
- throw new Error("No deployable contracts found in solc output (empty bytecode).");
458
- }
459
-
460
- // Stable ordering
461
- artifacts.sort((a, b) => a.contractName.localeCompare(b.contractName));
462
- return artifacts;
463
- }
464
-
465
- function _writeSolcArtifacts(outRoot, artifacts) {
466
- const artifactsDir = path.join(outRoot, "artifacts");
467
- _ensureDir(artifactsDir);
468
- for (const a of artifacts) {
469
- _writeText(path.join(artifactsDir, `${a.contractName}.abi.json`), JSON.stringify(a.abi, null, 2) + "\n");
470
- _writeText(path.join(artifactsDir, `${a.contractName}.bin`), a.bytecode + "\n");
471
- }
472
- }
473
-
474
- function _normalizeSolDoc(text) {
475
- if (!text) return "";
476
- return text
477
- .split(/\r?\n/g)
478
- .map((l) => l.trim())
479
- .filter(Boolean)
480
- .map((l) => {
481
- // Normalize common NatSpec tags into plain text (but keep @param/@return as-is)
482
- if (l.startsWith("@title ")) return l.slice("@title ".length).trim();
483
- if (l.startsWith("@notice ")) return l.slice("@notice ".length).trim();
484
- if (l.startsWith("@dev ")) return l.slice("@dev ".length).trim();
485
- return l;
486
- })
487
- .filter(Boolean)
488
- .join("\n")
489
- .trim();
490
- }
491
-
492
- function _extractSolDocs(solFilesAbs) {
493
- /** @type {Record<string, { contract?: string, functions: Record<string,string> }>} */
494
- const out = {};
495
-
496
- for (const file of solFilesAbs) {
497
- const src = fs.readFileSync(file, "utf8");
498
- const lines = src.split(/\r?\n/g);
499
-
500
- let pending = [];
501
- let inBlock = false;
502
- let currentContract = null;
503
-
504
- const flushPending = () => {
505
- const s = _normalizeSolDoc(pending.join("\n"));
506
- pending = [];
507
- return s;
508
- };
509
-
510
- for (let i = 0; i < lines.length; i++) {
511
- const raw = lines[i];
512
- const t = raw.trim();
513
-
514
- if (inBlock) {
515
- // Capture block comment content lines, stripping leading '*' and the closing '*/'
516
- const endIdx = raw.indexOf("*/");
517
- const content = endIdx !== -1 ? raw.slice(0, endIdx) : raw;
518
- const cleaned = content.replace(/^\s*\*\s?/, "").trim();
519
- if (cleaned) pending.push(cleaned);
520
- if (endIdx !== -1) inBlock = false;
521
- continue;
522
- }
523
-
524
- // Solidity NatSpec / doc comments
525
- if (t.startsWith("/**")) {
526
- inBlock = true;
527
- // Capture anything after '/**' on the same line and handle one-line comments.
528
- const startIdx = raw.indexOf("/**") + 3;
529
- const rest = raw.slice(startIdx);
530
- const endIdx = rest.indexOf("*/");
531
- const content = endIdx !== -1 ? rest.slice(0, endIdx) : rest;
532
- const cleaned = content.replace(/^\s*\*\s?/, "").trim();
533
- if (cleaned) pending.push(cleaned);
534
- if (endIdx !== -1) inBlock = false;
535
- continue;
536
- }
537
- if (t.startsWith("///")) {
538
- pending.push(t.slice(3).trim());
539
- continue;
540
- }
541
-
542
- // Skip empty lines without breaking pending docs
543
- if (!t) continue;
544
-
545
- // Contract/interface/library declaration
546
- const cMatch = t.match(/^(contract|interface|library)\s+([A-Za-z_][A-Za-z0-9_]*)\b/);
547
- if (cMatch) {
548
- const name = cMatch[2];
549
- const doc = flushPending();
550
- currentContract = name;
551
- if (!out[name]) out[name] = { contract: "", functions: {} };
552
- if (doc) out[name].contract = doc;
553
- continue;
554
- }
555
-
556
- // Function declaration
557
- const fMatch = t.match(/^function\s+([A-Za-z_][A-Za-z0-9_]*)\b/);
558
- if (fMatch && currentContract) {
559
- const fn = fMatch[1];
560
- const doc = flushPending();
561
- if (!out[currentContract]) out[currentContract] = { contract: "", functions: {} };
562
- if (doc) out[currentContract].functions[fn] = doc;
563
- continue;
564
- }
565
-
566
- // If we hit a real code line that's not a declaration, discard any pending doc
567
- pending = [];
568
- }
569
- }
570
-
571
- // Normalize all docs
572
- for (const k of Object.keys(out)) {
573
- out[k].contract = _normalizeSolDoc(out[k].contract || "");
574
- for (const fn of Object.keys(out[k].functions || {})) {
575
- out[k].functions[fn] = _normalizeSolDoc(out[k].functions[fn]);
576
- }
577
- }
578
-
579
- return out;
580
- }
581
-
582
- function _renderPackageIndexJs({ artifacts, entryDir }) {
583
- const dir = entryDir || "dist";
584
- const lines = [];
585
- lines.push("/**");
586
- lines.push(" * Auto-generated typed contract package.");
587
- lines.push(" *");
588
- lines.push(` * This file re-exports the package entry bundle in \`${dir}/\`.`);
589
- lines.push(" */");
590
- lines.push("");
591
- lines.push(`const entry = require("./${dir}");`);
592
- lines.push("Object.assign(exports, entry);");
593
- lines.push("");
594
-
595
- for (const a of artifacts) {
596
- const doc = a.docs && typeof a.docs.contract === "string" ? a.docs.contract : "";
597
- if (doc && doc.trim()) {
598
- lines.push("/**");
599
- for (const l of doc.split(/\r?\n/g)) {
600
- const t = l.trim();
601
- if (!t) continue;
602
- lines.push(` * ${t}`);
603
- }
604
- lines.push(" */");
605
- } else {
606
- lines.push("/**");
607
- lines.push(` * ${a.contractName}`);
608
- lines.push(" */");
609
- }
610
- lines.push(`exports.${a.contractName} = entry.${a.contractName};`);
611
- lines.push(`exports.${a.contractName}__factory = entry.${a.contractName}__factory;`);
612
- lines.push("");
613
- }
614
-
615
- return lines.join("\n") + "\n";
616
- }
617
-
618
- function _quoteIfNeeded(s) {
619
- if (typeof s !== "string") return s;
620
- return /[ \t"]/g.test(s) ? `"${s.replace(/"/g, '\\"')}"` : s;
621
- }
622
-
623
- function _npmCmd() {
624
- return process.platform === "win32" ? "npm.cmd" : "npm";
625
- }
626
-
627
- function _runNpm(args, cwd) {
628
- if (process.platform === "win32") {
629
- const cmd = `${_npmCmd()} ${args.map(_quoteIfNeeded).join(" ")}`;
630
- const res = spawnSync("cmd.exe", ["/d", "/s", "/c", cmd], { cwd, encoding: "utf8", stdio: "inherit" });
631
- if (res.error) throw res.error;
632
- if (res.status !== 0) throw new Error(`npm command failed: ${cmd}`);
633
- return;
634
- }
635
-
636
- const res = spawnSync(_npmCmd(), args, { cwd, encoding: "utf8", stdio: "inherit" });
637
- if (res.error) throw res.error;
638
- if (res.status !== 0) throw new Error(`npm command failed: ${_npmCmd()} ${args.join(" ")}`);
639
- }
640
-
641
- async function main() {
642
- const argv = process.argv.slice(2);
643
-
644
- if (argv.length === 0 || _hasFlag(argv, "--help") || _hasFlag(argv, "-h")) {
645
- process.stdout.write(_helpText());
646
- process.exitCode = 0;
647
- return;
648
- }
649
-
650
- const abiPathArg = _argValue(argv, "--abi");
651
- const binPathArg = _argValue(argv, "--bin");
652
- const solArg = _argValue(argv, "--sol") || _argValue(argv, "--sol-files") || _argValue(argv, "--solFiles");
653
- const solcArgsRaw = _argValue(argv, "--solc-args") || _argValue(argv, "--solcArgs");
654
- const artifactsJsonArg =
655
- _argValue(argv, "--artifacts-json") || _argValue(argv, "--artifactsJson") || _argValue(argv, "--artifacts");
656
- const outArg = _argValue(argv, "--out");
657
- const nameArg = _argValue(argv, "--name");
658
- const nonInteractive = _hasFlag(argv, "--non-interactive") || _hasFlag(argv, "--yes");
659
- const createPackageFlag = _hasFlag(argv, "--create-package");
660
- const pkgDirArg = _argValue(argv, "--package-dir");
661
- const pkgNameArg = _argValue(argv, "--package-name");
662
- const pkgDescArg = _argValue(argv, "--package-description") || "";
663
- const pkgAuthorArg = _argValue(argv, "--package-author") || "";
664
- const pkgLicenseArg = _argValue(argv, "--package-license") || "MIT";
665
- const pkgVersionArg = _argValue(argv, "--package-version") || "0.0.1";
666
- const lang = _normalizeLang(_argValue(argv, "--lang") || _argValue(argv, "--language") || _argValue(argv, "--type"));
667
-
668
- // Decide input type.
669
- let inputType = null; // "abibin" | "sol" | "artifactsjson"
670
- if (solArg) inputType = "sol";
671
- if (artifactsJsonArg) inputType = inputType || "artifactsjson";
672
- if (abiPathArg || binPathArg) inputType = inputType || "abibin";
673
- if ((solArg ? 1 : 0) + (artifactsJsonArg ? 1 : 0) + (abiPathArg || binPathArg ? 1 : 0) > 1) {
674
- throw new Error("Select only one input mode: --abi/--bin OR --sol OR --artifacts-json.");
675
- }
676
-
677
- let createPackage = false;
678
- let outDir = outArg ? path.resolve(outArg) : null;
679
- let contractName = nameArg || null;
680
-
681
- // Interactive prompt: ask input type first.
682
- let absAbi = null;
683
- let absBin = null;
684
- let solFilesAbs = [];
685
- let artifactsJsonAbs = null;
686
- const solcExtraArgs = solcArgsRaw ? _parseSolcExtraArgs(solcArgsRaw) : [];
687
-
688
- if (!nonInteractive) {
689
- const rl0 = readline.createInterface({ input: stdin, output: stdout });
690
- try {
691
- if (!inputType) {
692
- const ans = (await rl0.question("Input type? (1) ABI+BIN (2) SOL (3) ARTIFACTS JSON ")).trim();
693
- inputType = ans === "2" ? "sol" : ans === "3" ? "artifactsjson" : "abibin";
694
- }
695
- if (inputType === "abibin") {
696
- const abiP = abiPathArg || (await rl0.question("Enter ABI path (.json): ")).trim();
697
- const binP = binPathArg || (await rl0.question("Enter bytecode path (.bin): ")).trim();
698
- if (!abiP || !binP) throw new Error("Missing ABI/BIN paths.");
699
- absAbi = path.resolve(abiP);
700
- absBin = path.resolve(binP);
701
- if (!fs.existsSync(absAbi)) throw new Error(`ABI file not found: ${absAbi}`);
702
- if (!fs.existsSync(absBin)) throw new Error(`Bytecode file not found: ${absBin}`);
703
- contractName = contractName || _cap(_defaultContractNameFromAbiPath(absAbi));
704
- } else if (inputType === "sol") {
705
- const solP = solArg || (await rl0.question("Enter Solidity file(s) (comma-separated): ")).trim();
706
- const files = _parseCommaSeparatedFiles(solP);
707
- if (files.length === 0) throw new Error("Missing Solidity file(s).");
708
- solFilesAbs = files.map((p) => path.resolve(p));
709
- for (const f of solFilesAbs) {
710
- if (!fs.existsSync(f)) throw new Error(`Solidity file not found: ${f}`);
711
- }
712
- if (!contractName) {
713
- const maybe = (await rl0.question("Contract name (optional; empty = generate all): ")).trim();
714
- if (maybe) contractName = maybe;
715
- }
716
- } else {
717
- const p = artifactsJsonArg || (await rl0.question("Enter artifacts JSON path (.json): ")).trim();
718
- if (!p) throw new Error("Missing artifacts JSON path.");
719
- artifactsJsonAbs = path.resolve(p);
720
- if (!fs.existsSync(artifactsJsonAbs)) throw new Error(`Artifacts JSON file not found: ${artifactsJsonAbs}`);
721
- }
722
- } finally {
723
- rl0.close();
724
- }
725
- }
726
-
727
- // Non-interactive input validation
728
- if (nonInteractive) {
729
- if (!inputType) {
730
- throw new Error("Select an input type: pass --abi/--bin OR pass --sol <file1.sol,file2.sol> OR pass --artifacts-json <file.json>.");
731
- }
732
- if (inputType === "abibin") {
733
- if (!abiPathArg || !binPathArg) {
734
- throw new Error("Missing required arguments: --abi <path> --bin <path>");
735
- }
736
- absAbi = path.resolve(abiPathArg);
737
- absBin = path.resolve(binPathArg);
738
- if (!fs.existsSync(absAbi)) throw new Error(`ABI file not found: ${absAbi}`);
739
- if (!fs.existsSync(absBin)) throw new Error(`Bytecode file not found: ${absBin}`);
740
- contractName = contractName || _cap(_defaultContractNameFromAbiPath(absAbi));
741
- } else if (inputType === "sol") {
742
- const files = _parseCommaSeparatedFiles(solArg);
743
- if (files.length === 0) throw new Error("Missing required argument: --sol <file1.sol,file2.sol>");
744
- solFilesAbs = files.map((p) => path.resolve(p));
745
- for (const f of solFilesAbs) {
746
- if (!fs.existsSync(f)) throw new Error(`Solidity file not found: ${f}`);
747
- }
748
- // contractName can be null => generate all
749
- } else {
750
- if (!artifactsJsonArg) throw new Error("Missing required argument: --artifacts-json <path/to/artifacts.json>");
751
- artifactsJsonAbs = path.resolve(artifactsJsonArg);
752
- if (!fs.existsSync(artifactsJsonAbs)) throw new Error(`Artifacts JSON file not found: ${artifactsJsonAbs}`);
753
- }
754
- }
755
-
756
- if (createPackageFlag) {
757
- createPackage = true;
758
- }
759
-
760
- if (!nonInteractive && !createPackageFlag) {
761
- const rl = readline.createInterface({ input: stdin, output: stdout });
762
- try {
763
- const ans = (await rl.question("Do you want to create a new package? (Y/N) ")).trim().toLowerCase();
764
- createPackage = ans === "y" || ans === "yes";
765
-
766
- if (createPackage) {
767
- const pkgFolder = (await rl.question("Enter the folder path where the package should be created: ")).trim();
768
- const pkgName = (await rl.question("Enter package name: ")).trim();
769
- const pkgDesc = (await rl.question("Enter package description: ")).trim();
770
- const pkgAuthor = (await rl.question("Enter author name: ")).trim();
771
- const pkgLicense = (await rl.question("Enter license (default: MIT): ")).trim() || "MIT";
772
- const pkgVersion = (await rl.question("Enter version (default: 0.0.1): ")).trim() || "0.0.1";
773
-
774
- outDir = path.resolve(pkgFolder, pkgName);
775
- _ensureDir(outDir);
776
-
777
- _createPackageScaffold({
778
- outDir,
779
- pkgName,
780
- pkgDesc,
781
- pkgAuthor,
782
- pkgLicense,
783
- pkgVersion,
784
- lang,
785
- });
786
- } else {
787
- const target = (await rl.question("Enter the location in your existing package (relative to package root): ")).trim();
788
- outDir = path.resolve(process.cwd(), target || "src/contracts");
789
- }
790
- } finally {
791
- rl.close();
792
- }
793
- }
794
-
795
- if (createPackage && nonInteractive) {
796
- const pkgFolder = pkgDirArg ? path.resolve(pkgDirArg) : null;
797
- const pkgName = pkgNameArg || null;
798
- if (!pkgFolder || !pkgName) {
799
- throw new Error("When using --create-package in non-interactive mode, pass --package-dir <dir> and --package-name <name>.");
800
- }
801
- outDir = path.resolve(pkgFolder, pkgName);
802
- _ensureDir(outDir);
803
- _createPackageScaffold({
804
- outDir,
805
- pkgName,
806
- pkgDesc: pkgDescArg,
807
- pkgAuthor: pkgAuthorArg,
808
- pkgLicense: pkgLicenseArg,
809
- pkgVersion: pkgVersionArg,
810
- lang,
811
- });
812
- }
813
-
814
- if (!outDir) outDir = path.resolve(process.cwd(), "generated-contract");
815
-
816
- const targetSrcDir = createPackage ? path.join(outDir, "src") : outDir;
817
- _ensureDir(targetSrcDir);
818
-
819
- // Resolve compilation / artifacts
820
- /** @type {Array<{ contractName: string, abi: any[], bytecode: string }>} */
821
- let artifacts = [];
822
- if (inputType === "abibin") {
823
- const abi = _readJson(absAbi);
824
- const bytecodeRaw = fs.readFileSync(absBin, "utf8").trim();
825
- const bytecode = bytecodeRaw.startsWith("0x") ? bytecodeRaw : `0x${bytecodeRaw}`;
826
- artifacts = [{ contractName, abi, bytecode, docs: null }];
827
- } else if (inputType === "sol") {
828
- const solcPath = _resolveSolcPath(argv);
829
- artifacts = _compileSolidityToArtifacts({ solcPath, solFilesAbs, contractNameFilter: contractName, solcExtraArgs });
830
-
831
- // Attach Solidity doc comments (NatSpec) to artifacts
832
- const docsByContract = _extractSolDocs(solFilesAbs);
833
- for (const a of artifacts) {
834
- a.docs = docsByContract[a.contractName] || null;
835
- }
836
-
837
- // As requested: emit ABI/BIN artifacts to disk
838
- _writeSolcArtifacts(createPackage ? outDir : targetSrcDir, artifacts);
839
- } else {
840
- // Artifacts JSON
841
- artifacts = _readArtifactsJson(artifactsJsonAbs || path.resolve(artifactsJsonArg));
842
- }
843
-
844
- if (artifacts.length === 1) {
845
- // Keep the old API/behavior for single-contract generation.
846
- const a = artifacts[0];
847
- generateFromArtifacts({ outDir: targetSrcDir, artifacts: [a], lang });
848
- } else {
849
- generateFromArtifacts({ outDir: targetSrcDir, artifacts, lang });
850
- }
851
-
852
- if (createPackage) {
853
- _writeText(
854
- path.join(outDir, "README.md"),
855
- _packageReadme({
856
- pkgName: pkgNameArg || path.basename(outDir),
857
- pkgDesc: pkgDescArg,
858
- artifacts,
859
- createdFromSolidity: inputType === "sol",
860
- lang,
861
- }),
862
- );
863
-
864
- // Transactional tests (always per-contract)
865
- _ensureDir(path.join(outDir, "test", "e2e"));
866
- _ensureDir(path.join(outDir, "examples"));
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
-
877
- for (const a of artifacts) {
878
- _writeText(
879
- path.join(outDir, "test", "e2e", `${a.contractName}.e2e.test.js`),
880
- generateTransactionalTestJs({ contractName: a.contractName, abi: a.abi }),
881
- );
882
- }
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
-
891
- if (artifacts.length === 1) {
892
- // Back-compat: keep original example filenames for a single-contract package.
893
- const a = artifacts[0];
894
- const ctor = _findConstructor(a.abi);
895
- const ctorArgsExpr = (ctor.inputs || []).map((i) => _solTypeToExampleValueExpr(i)).join(", ");
896
-
897
- _writeText(
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
- `,
943
- );
944
-
945
- _writeText(
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
- `,
994
- );
995
-
996
- _writeText(
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
- `,
1045
- );
1046
-
1047
- _writeText(
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
- `,
1176
- );
1177
- } else {
1178
- // Multi-contract: avoid filename collisions.
1179
- for (const a of artifacts) {
1180
- const ctor = _findConstructor(a.abi);
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
- );
1187
-
1188
- _writeText(
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`,
1191
- );
1192
-
1193
- _writeText(
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`,
1196
- );
1197
-
1198
- _writeText(
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`,
1201
- );
1202
-
1203
- _writeText(
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`,
1206
- );
1207
- }
1208
- }
1209
-
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" }));
1212
- }
1213
-
1214
- // Final step: after package creation, emit types.
1215
- if (createPackage) {
1216
- _runNpm(["install", "--no-fund", "--no-audit"], outDir);
1217
- if (lang === "ts") {
1218
- _runNpm(["run", "build:ts"], outDir);
1219
- }
1220
- }
1221
-
1222
- console.warn("This is an experimental SDK. Use at your own risk.");
1223
- console.log(`Generated contract files in: ${targetSrcDir}`);
1224
- }
1225
-
1226
- function _cap(s) {
1227
- return s ? s[0].toUpperCase() + s.slice(1) : s;
1228
- }
1229
-
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
- );
1381
- }
1382
-
1383
- function _createPackageScaffold({ outDir, pkgName, pkgDesc, pkgAuthor, pkgLicense, pkgVersion, lang = "ts" }) {
1384
- _ensureDir(outDir);
1385
- _ensureDir(path.join(outDir, "src"));
1386
- _ensureDir(path.join(outDir, "test", "e2e"));
1387
- _ensureDir(path.join(outDir, "examples"));
1388
-
1389
- const outLang = _normalizeLang(lang);
1390
- const isTs = outLang === "ts";
1391
-
1392
- const rootPkg = _readRootPackageJson();
1393
- const rootDeps = _rewriteFileDepsToAbsolute(rootPkg.dependencies || {}, __dirname);
1394
-
1395
- // Ensure the generated package depends on this repo's quantumcoin via absolute file path.
1396
- rootDeps.quantumcoin = `file:${__dirname.replace(/\\\\/g, "/")}`;
1397
-
1398
- const pkgJson = {
1399
- name: pkgName,
1400
- version: pkgVersion,
1401
- description: pkgDesc,
1402
- author: pkgAuthor,
1403
- license: pkgLicense,
1404
- main: isTs ? "dist/index.js" : "src/index.js",
1405
- types: isTs ? "dist/index.d.ts" : "src/index.d.ts",
1406
- scripts: {
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
- }),
1421
- },
1422
- dependencies: rootDeps,
1423
- devDependencies: {},
1424
- };
1425
-
1426
- _writeJson(path.join(outDir, "package.json"), pkgJson);
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
- }
1442
-
1443
- _writeText(
1444
- path.join(outDir, "README.md"),
1445
- _packageReadme({ pkgName, pkgDesc, artifacts: [], createdFromSolidity: false, lang: outLang }),
1446
- );
1447
-
1448
- _writeText(path.join(outDir, ".gitignore"), `node_modules\n/dist\n*.log\n`);
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
-
1454
- // Minimal shims so the generated TypeScript can compile even though `quantumcoin`
1455
- // is a JavaScript package (no bundled .d.ts in this repo).
1456
- _writeText(
1457
- path.join(outDir, "src", "quantumcoin-shims.d.ts"),
1458
- `declare module "quantumcoin" {\n` +
1459
- ` export type ContractRunner = any;\n` +
1460
- ` export type TransactionResponse = any;\n` +
1461
- ` export type ContractTransactionResponse = any;\n` +
1462
- ` export type TransactionRequest = any;\n` +
1463
- `\n` +
1464
- ` export class Contract {\n` +
1465
- ` constructor(address: string, abi: any, runner?: any, bytecode?: any);\n` +
1466
- ` target: string;\n` +
1467
- ` address: string;\n` +
1468
- ` interface: any;\n` +
1469
- ` populateTransaction: any;\n` +
1470
- ` call(methodName: string, args: any[], overrides?: TransactionRequest): Promise<any>;\n` +
1471
- ` send(methodName: string, args: any[], overrides?: TransactionRequest): Promise<ContractTransactionResponse>;\n` +
1472
- ` deployTransaction(): TransactionResponse | null;\n` +
1473
- ` }\n` +
1474
- `\n` +
1475
- ` export class ContractFactory {\n` +
1476
- ` signer: any;\n` +
1477
- ` constructor(abi: any, bytecode: string, signer: any);\n` +
1478
- ` getDeployTransaction(...args: any[]): TransactionRequest;\n` +
1479
- ` }\n` +
1480
- `\n` +
1481
- ` export function getCreateAddress(opts: { from: string; nonce: number }): string;\n` +
1482
- `}\n`,
1483
- );
1484
- }
1485
-
1486
- main().catch((e) => {
1487
- console.error(e);
1488
- process.exitCode = 1;
1489
- });
1490
-
1
+ #!/usr/bin/env node
2
+ /**
3
+ * sdkgen
4
+ *
5
+ * NOTE: This script is the SDK generator CLI entrypoint.
6
+ *
7
+ * SPEC.md section 15: Typed Contract Generator
8
+ *
9
+ * Usage:
10
+ * - Non-interactive:
11
+ * `node generate-sdk.js --abi path/to/abi.json --bin path/to/bytecode.bin --out ./out --name MyContract`
12
+ *
13
+ * - Interactive:
14
+ * `node generate-sdk.js --abi path/to/abi.json --bin path/to/bytecode.bin`
15
+ */
16
+
17
+ const fs = require("node:fs");
18
+ const path = require("node:path");
19
+ const readline = require("node:readline/promises");
20
+ const { stdin, stdout } = require("node:process");
21
+ const { execFileSync, spawnSync } = require("node:child_process");
22
+
23
+ const { generate, generateFromArtifacts, generateTransactionalTestJs, generateAllContractsTransactionalTestJs } = require("./src/generator");
24
+
25
+ function _helpText() {
26
+ return `
27
+ sdkgen (generate-sdk.js)
28
+
29
+ Generates TypeScript or JavaScript contract wrappers (plus optional package scaffold, examples and transactional tests).
30
+
31
+ If you run this script with no parameters, it prints this help.
32
+
33
+ USAGE
34
+ node generate-sdk.js [options]
35
+
36
+ INPUT MODES
37
+ 1) ABI + BIN (bytecode)
38
+ --abi <path/to/Contract.abi.json>
39
+ --bin <path/to/Contract.bin>
40
+ --name <ContractName> (optional; defaults from ABI filename)
41
+
42
+ 2) Solidity (.sol) sources
43
+ --sol "<A.sol,B.sol,...>" (comma-separated list; can be 1+ files)
44
+ --name <ContractName> (optional; if omitted, generates ALL deployable contracts found)
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")
47
+
48
+ 3) Artifacts JSON (array of ABI+BIN pairs)
49
+ --artifacts-json <path/to/artifacts.json>
50
+ The JSON file should be an array. Each entry supports:
51
+ - { abi: "<path|abiJsonString|abiArray>", bin: "<path|0x...>", name?: "<ContractName>" }
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
+ ]
75
+
76
+ PACKAGE OUTPUT (optional)
77
+ --create-package
78
+ --package-dir <folder>
79
+ --package-name <name>
80
+ --package-description <text>
81
+ --package-author <text>
82
+ --package-license <spdx> (default: MIT)
83
+ --package-version <semver> (default: 0.0.1)
84
+
85
+ Notes:
86
+ - When --create-package is used, the generator creates a complete npm package scaffold with:
87
+ - src/*.(ts|js) (contracts + factories; depends on --lang)
88
+ - test/e2e/*.e2e.test.js (transactional tests)
89
+ - examples/ (deploy, read-operations, write-operations, offline-signing, events in .js and .ts)
90
+ - artifacts/ (only when using --sol)
91
+ - index.js + index.d.ts (package entry shims; TS mode uses dist/)
92
+ - For --lang ts, the generator will run:
93
+ npm install
94
+ npm run build:ts
95
+ as the final step.
96
+ - For --lang js, the generator will run:
97
+ npm install
98
+ and no build step is required.
99
+
100
+ GENERAL OPTIONS
101
+ --out <folder> Output folder (default: ./generated-contract)
102
+ --lang <ts|js> Output language for generated contract sources (default: ts)
103
+ --non-interactive | --yes Disable prompts (required for CI)
104
+ -h | --help Show this help
105
+
106
+ ENVIRONMENT
107
+ QC_SOLC_PATH / SOLC / SOLC_PATH
108
+ Path to solc executable used when compiling Solidity.
109
+ Default: c:\\solc\\solc.exe
110
+
111
+ QC_RPC_URL / QC_CHAIN_ID
112
+ Used by the auto-generated transactional tests in the generated package.
113
+
114
+ EXAMPLES
115
+ Generate typed files from ABI+BIN into ./out:
116
+ node generate-sdk.js --abi .\\artifacts\\MyContract.abi.json --bin .\\artifacts\\MyContract.bin --out .\\out --name MyContract --non-interactive
117
+
118
+ Generate a new typed package from ABI+BIN:
119
+ node generate-sdk.js --abi .\\My.abi.json --bin .\\My.bin --name MyContract --create-package --package-dir .\\tmp --package-name my-typed-contract --package-description "My typed contract" --package-author "me" --non-interactive
120
+
121
+ Generate a new typed package from Solidity (single file):
122
+ set QC_SOLC_PATH=c:\\solc\\solc.exe
123
+ node generate-sdk.js --sol ".\\contracts\\MyContract.sol" --create-package --package-dir .\\tmp --package-name my-typed-contract --package-description "My typed contract" --package-author "me" --non-interactive
124
+
125
+ Generate a new typed package from Solidity (multiple files):
126
+ node generate-sdk.js --sol ".\\contracts\\A.sol,.\\contracts\\B.sol,.\\contracts\\lib\\C.sol" --create-package --package-dir .\\tmp --package-name my-typed-contract --package-description "My typed contract" --package-author "me" --non-interactive
127
+
128
+ `.trimStart();
129
+ }
130
+
131
+ function _argValue(argv, name) {
132
+ const idx = argv.indexOf(name);
133
+ if (idx === -1) return null;
134
+ return argv[idx + 1] || null;
135
+ }
136
+
137
+ function _hasFlag(argv, name) {
138
+ return argv.includes(name);
139
+ }
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
+
149
+ function _basenameNoExt(p) {
150
+ const b = path.basename(p);
151
+ const i = b.lastIndexOf(".");
152
+ return i === -1 ? b : b.slice(0, i);
153
+ }
154
+
155
+ function _defaultContractNameFromAbiPath(abiPath) {
156
+ const b = path.basename(abiPath);
157
+ if (/\.abi\.json$/i.test(b)) return b.replace(/\.abi\.json$/i, "");
158
+ return _basenameNoExt(abiPath);
159
+ }
160
+
161
+ function _ensureDir(p) {
162
+ fs.mkdirSync(p, { recursive: true });
163
+ }
164
+
165
+ function _readRootDependencies() {
166
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8"));
167
+ return pkg.dependencies || {};
168
+ }
169
+
170
+ function _readRootPackageJson() {
171
+ return JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8"));
172
+ }
173
+
174
+ function _readJson(filePath) {
175
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
176
+ }
177
+
178
+ function _looksLikeHex(s) {
179
+ if (typeof s !== "string") return false;
180
+ const t = s.trim();
181
+ if (!t) return false;
182
+ if (t.startsWith("0x")) return /^0x[0-9a-fA-F]*$/.test(t);
183
+ return /^[0-9a-fA-F]*$/.test(t);
184
+ }
185
+
186
+ function _readArtifactsJson(fileAbs) {
187
+ const baseDir = path.dirname(fileAbs);
188
+ const parsed = _readJson(fileAbs);
189
+ if (!Array.isArray(parsed)) throw new Error(`Artifacts JSON must be an array: ${fileAbs}`);
190
+
191
+ /** @type {Array<{ contractName: string, abi: any[], bytecode: string, docs?: any }>} */
192
+ const artifacts = [];
193
+
194
+ for (let i = 0; i < parsed.length; i++) {
195
+ const entry = parsed[i];
196
+ if (!entry || typeof entry !== "object") throw new Error(`Invalid artifacts entry at index ${i} (expected object)`);
197
+
198
+ const abiField = entry.abi ?? entry.abiPath;
199
+ const binField = entry.bin ?? entry.bytecode ?? entry.binPath ?? entry.bytecodePath;
200
+ const nameField = entry.name ?? entry.contractName;
201
+
202
+ let abi;
203
+ let abiPath = null;
204
+ if (typeof abiField === "string") {
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
+ }
221
+ } else if (Array.isArray(abiField)) {
222
+ abi = abiField;
223
+ } else {
224
+ throw new Error(`Invalid "abi" for artifacts entry ${i} (expected path, ABI JSON string, or ABI array)`);
225
+ }
226
+ if (!Array.isArray(abi)) throw new Error(`ABI must be an array (entry ${i})`);
227
+
228
+ let bytecodeRaw;
229
+ if (typeof binField === "string") {
230
+ const maybePath = path.resolve(baseDir, binField);
231
+ if (fs.existsSync(maybePath) && fs.statSync(maybePath).isFile()) {
232
+ bytecodeRaw = fs.readFileSync(maybePath, "utf8").trim();
233
+ } else if (_looksLikeHex(binField)) {
234
+ bytecodeRaw = binField.trim();
235
+ } else {
236
+ throw new Error(`BIN/bytecode file not found (entry ${i}): ${maybePath}`);
237
+ }
238
+ } else {
239
+ throw new Error(`Invalid "bin" for artifacts entry ${i} (expected path or hex string)`);
240
+ }
241
+
242
+ const bytecode = bytecodeRaw.startsWith("0x") ? bytecodeRaw : `0x${bytecodeRaw}`;
243
+
244
+ let contractName = null;
245
+ if (typeof nameField === "string" && nameField.trim()) {
246
+ contractName = _cap(nameField.trim());
247
+ } else if (abiPath) {
248
+ contractName = _cap(_defaultContractNameFromAbiPath(abiPath));
249
+ } else {
250
+ throw new Error(`Missing contract name for artifacts entry ${i}. Provide "name" or use abi as a path.`);
251
+ }
252
+
253
+ artifacts.push({ contractName, abi, bytecode, docs: null });
254
+ }
255
+
256
+ // Ensure stable ordering and unique names
257
+ artifacts.sort((a, b) => a.contractName.localeCompare(b.contractName));
258
+ const seen = new Set();
259
+ for (const a of artifacts) {
260
+ if (seen.has(a.contractName)) throw new Error(`Duplicate contractName in artifacts JSON: ${a.contractName}`);
261
+ seen.add(a.contractName);
262
+ }
263
+
264
+ if (artifacts.length === 0) throw new Error("Artifacts JSON contained no entries.");
265
+ return artifacts;
266
+ }
267
+
268
+ function _rewriteFileDepsToAbsolute(deps, rootDir) {
269
+ const out = { ...deps };
270
+ for (const [name, ver] of Object.entries(out)) {
271
+ if (typeof ver === "string" && ver.startsWith("file:")) {
272
+ const rel = ver.slice("file:".length);
273
+ const abs = path.resolve(rootDir, rel);
274
+ out[name] = `file:${abs.replace(/\\\\/g, "/")}`;
275
+ }
276
+ }
277
+ return out;
278
+ }
279
+
280
+ function _writeJson(filePath, obj) {
281
+ fs.writeFileSync(filePath, JSON.stringify(obj, null, 2) + "\n", "utf8");
282
+ }
283
+
284
+ function _writeText(filePath, content) {
285
+ fs.writeFileSync(filePath, content, "utf8");
286
+ }
287
+
288
+ function _findConstructor(abi) {
289
+ const ctor = (abi || []).find((f) => f && f.type === "constructor");
290
+ return ctor || { type: "constructor", inputs: [] };
291
+ }
292
+
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)
299
+ if (type.endsWith("]")) {
300
+ const inner = type.slice(0, type.lastIndexOf("["));
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}]`;
311
+ }
312
+
313
+ if (type === "address") return "wallet.address";
314
+ if (type === "bool") return "true";
315
+ if (type === "string") return JSON.stringify("hello");
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
+
339
+ return "undefined";
340
+ }
341
+
342
+ function _parseCommaSeparatedFiles(v) {
343
+ if (!v) return [];
344
+ return v
345
+ .split(",")
346
+ .map((s) => s.trim())
347
+ .filter(Boolean);
348
+ }
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
+
413
+ function _resolveSolcPath(argv) {
414
+ const solcArg = _argValue(argv, "--solc") || _argValue(argv, "--solc-path") || _argValue(argv, "--solcPath");
415
+ const env =
416
+ solcArg ||
417
+ process.env.QC_SOLC_PATH ||
418
+ process.env.SOLC ||
419
+ process.env.SOLC_PATH ||
420
+ process.env.solc ||
421
+ null;
422
+ return env || "c:\\solc\\solc.exe";
423
+ }
424
+
425
+ function _assertSolcExists(solcPath) {
426
+ if (!fs.existsSync(solcPath)) {
427
+ throw new Error(
428
+ `solc not found at ${solcPath}. Set env QC_SOLC_PATH or SOLC (e.g. c:\\\\solc\\\\solc.exe) or pass --solc <path>.`,
429
+ );
430
+ }
431
+ }
432
+
433
+ function _compileSolidityToArtifacts({ solcPath, solFilesAbs, contractNameFilter, solcExtraArgs }) {
434
+ _assertSolcExists(solcPath);
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" });
438
+ const parsed = JSON.parse(out);
439
+
440
+ const artifacts = [];
441
+ for (const key of Object.keys(parsed.contracts || {})) {
442
+ const entry = parsed.contracts[key];
443
+ const name = key.includes(":") ? key.split(":").pop() : key;
444
+ if (!name) continue;
445
+ if (contractNameFilter && name !== contractNameFilter) continue;
446
+ const abi = entry && entry.abi ? JSON.parse(entry.abi) : null;
447
+ const bin = entry && typeof entry.bin === "string" ? entry.bin : "";
448
+ if (!abi || !Array.isArray(abi)) continue;
449
+ if (!bin) continue; // skip abstract/interfaces
450
+ artifacts.push({ contractName: name, abi, bytecode: bin.startsWith("0x") ? bin : `0x${bin}` });
451
+ }
452
+
453
+ if (contractNameFilter && artifacts.length === 0) {
454
+ throw new Error(`No compiled contract matched --name ${contractNameFilter}`);
455
+ }
456
+ if (artifacts.length === 0) {
457
+ throw new Error("No deployable contracts found in solc output (empty bytecode).");
458
+ }
459
+
460
+ // Stable ordering
461
+ artifacts.sort((a, b) => a.contractName.localeCompare(b.contractName));
462
+ return artifacts;
463
+ }
464
+
465
+ function _writeSolcArtifacts(outRoot, artifacts) {
466
+ const artifactsDir = path.join(outRoot, "artifacts");
467
+ _ensureDir(artifactsDir);
468
+ for (const a of artifacts) {
469
+ _writeText(path.join(artifactsDir, `${a.contractName}.abi.json`), JSON.stringify(a.abi, null, 2) + "\n");
470
+ _writeText(path.join(artifactsDir, `${a.contractName}.bin`), a.bytecode + "\n");
471
+ }
472
+ }
473
+
474
+ function _normalizeSolDoc(text) {
475
+ if (!text) return "";
476
+ return text
477
+ .split(/\r?\n/g)
478
+ .map((l) => l.trim())
479
+ .filter(Boolean)
480
+ .map((l) => {
481
+ // Normalize common NatSpec tags into plain text (but keep @param/@return as-is)
482
+ if (l.startsWith("@title ")) return l.slice("@title ".length).trim();
483
+ if (l.startsWith("@notice ")) return l.slice("@notice ".length).trim();
484
+ if (l.startsWith("@dev ")) return l.slice("@dev ".length).trim();
485
+ return l;
486
+ })
487
+ .filter(Boolean)
488
+ .join("\n")
489
+ .trim();
490
+ }
491
+
492
+ function _extractSolDocs(solFilesAbs) {
493
+ /** @type {Record<string, { contract?: string, functions: Record<string,string> }>} */
494
+ const out = {};
495
+
496
+ for (const file of solFilesAbs) {
497
+ const src = fs.readFileSync(file, "utf8");
498
+ const lines = src.split(/\r?\n/g);
499
+
500
+ let pending = [];
501
+ let inBlock = false;
502
+ let currentContract = null;
503
+
504
+ const flushPending = () => {
505
+ const s = _normalizeSolDoc(pending.join("\n"));
506
+ pending = [];
507
+ return s;
508
+ };
509
+
510
+ for (let i = 0; i < lines.length; i++) {
511
+ const raw = lines[i];
512
+ const t = raw.trim();
513
+
514
+ if (inBlock) {
515
+ // Capture block comment content lines, stripping leading '*' and the closing '*/'
516
+ const endIdx = raw.indexOf("*/");
517
+ const content = endIdx !== -1 ? raw.slice(0, endIdx) : raw;
518
+ const cleaned = content.replace(/^\s*\*\s?/, "").trim();
519
+ if (cleaned) pending.push(cleaned);
520
+ if (endIdx !== -1) inBlock = false;
521
+ continue;
522
+ }
523
+
524
+ // Solidity NatSpec / doc comments
525
+ if (t.startsWith("/**")) {
526
+ inBlock = true;
527
+ // Capture anything after '/**' on the same line and handle one-line comments.
528
+ const startIdx = raw.indexOf("/**") + 3;
529
+ const rest = raw.slice(startIdx);
530
+ const endIdx = rest.indexOf("*/");
531
+ const content = endIdx !== -1 ? rest.slice(0, endIdx) : rest;
532
+ const cleaned = content.replace(/^\s*\*\s?/, "").trim();
533
+ if (cleaned) pending.push(cleaned);
534
+ if (endIdx !== -1) inBlock = false;
535
+ continue;
536
+ }
537
+ if (t.startsWith("///")) {
538
+ pending.push(t.slice(3).trim());
539
+ continue;
540
+ }
541
+
542
+ // Skip empty lines without breaking pending docs
543
+ if (!t) continue;
544
+
545
+ // Contract/interface/library declaration
546
+ const cMatch = t.match(/^(contract|interface|library)\s+([A-Za-z_][A-Za-z0-9_]*)\b/);
547
+ if (cMatch) {
548
+ const name = cMatch[2];
549
+ const doc = flushPending();
550
+ currentContract = name;
551
+ if (!out[name]) out[name] = { contract: "", functions: {} };
552
+ if (doc) out[name].contract = doc;
553
+ continue;
554
+ }
555
+
556
+ // Function declaration
557
+ const fMatch = t.match(/^function\s+([A-Za-z_][A-Za-z0-9_]*)\b/);
558
+ if (fMatch && currentContract) {
559
+ const fn = fMatch[1];
560
+ const doc = flushPending();
561
+ if (!out[currentContract]) out[currentContract] = { contract: "", functions: {} };
562
+ if (doc) out[currentContract].functions[fn] = doc;
563
+ continue;
564
+ }
565
+
566
+ // If we hit a real code line that's not a declaration, discard any pending doc
567
+ pending = [];
568
+ }
569
+ }
570
+
571
+ // Normalize all docs
572
+ for (const k of Object.keys(out)) {
573
+ out[k].contract = _normalizeSolDoc(out[k].contract || "");
574
+ for (const fn of Object.keys(out[k].functions || {})) {
575
+ out[k].functions[fn] = _normalizeSolDoc(out[k].functions[fn]);
576
+ }
577
+ }
578
+
579
+ return out;
580
+ }
581
+
582
+ function _renderPackageIndexJs({ artifacts, entryDir }) {
583
+ const dir = entryDir || "dist";
584
+ const lines = [];
585
+ lines.push("/**");
586
+ lines.push(" * Auto-generated typed contract package.");
587
+ lines.push(" *");
588
+ lines.push(` * This file re-exports the package entry bundle in \`${dir}/\`.`);
589
+ lines.push(" */");
590
+ lines.push("");
591
+ lines.push(`const entry = require("./${dir}");`);
592
+ lines.push("Object.assign(exports, entry);");
593
+ lines.push("");
594
+
595
+ for (const a of artifacts) {
596
+ const doc = a.docs && typeof a.docs.contract === "string" ? a.docs.contract : "";
597
+ if (doc && doc.trim()) {
598
+ lines.push("/**");
599
+ for (const l of doc.split(/\r?\n/g)) {
600
+ const t = l.trim();
601
+ if (!t) continue;
602
+ lines.push(` * ${t}`);
603
+ }
604
+ lines.push(" */");
605
+ } else {
606
+ lines.push("/**");
607
+ lines.push(` * ${a.contractName}`);
608
+ lines.push(" */");
609
+ }
610
+ lines.push(`exports.${a.contractName} = entry.${a.contractName};`);
611
+ lines.push(`exports.${a.contractName}__factory = entry.${a.contractName}__factory;`);
612
+ lines.push("");
613
+ }
614
+
615
+ return lines.join("\n") + "\n";
616
+ }
617
+
618
+ function _quoteIfNeeded(s) {
619
+ if (typeof s !== "string") return s;
620
+ return /[ \t"]/g.test(s) ? `"${s.replace(/"/g, '\\"')}"` : s;
621
+ }
622
+
623
+ function _npmCmd() {
624
+ return process.platform === "win32" ? "npm.cmd" : "npm";
625
+ }
626
+
627
+ function _runNpm(args, cwd) {
628
+ if (process.platform === "win32") {
629
+ const cmd = `${_npmCmd()} ${args.map(_quoteIfNeeded).join(" ")}`;
630
+ const res = spawnSync("cmd.exe", ["/d", "/s", "/c", cmd], { cwd, encoding: "utf8", stdio: "inherit" });
631
+ if (res.error) throw res.error;
632
+ if (res.status !== 0) throw new Error(`npm command failed: ${cmd}`);
633
+ return;
634
+ }
635
+
636
+ const res = spawnSync(_npmCmd(), args, { cwd, encoding: "utf8", stdio: "inherit" });
637
+ if (res.error) throw res.error;
638
+ if (res.status !== 0) throw new Error(`npm command failed: ${_npmCmd()} ${args.join(" ")}`);
639
+ }
640
+
641
+ async function main() {
642
+ const argv = process.argv.slice(2);
643
+
644
+ if (argv.length === 0 || _hasFlag(argv, "--help") || _hasFlag(argv, "-h")) {
645
+ process.stdout.write(_helpText());
646
+ process.exitCode = 0;
647
+ return;
648
+ }
649
+
650
+ const abiPathArg = _argValue(argv, "--abi");
651
+ const binPathArg = _argValue(argv, "--bin");
652
+ const solArg = _argValue(argv, "--sol") || _argValue(argv, "--sol-files") || _argValue(argv, "--solFiles");
653
+ const solcArgsRaw = _argValue(argv, "--solc-args") || _argValue(argv, "--solcArgs");
654
+ const artifactsJsonArg =
655
+ _argValue(argv, "--artifacts-json") || _argValue(argv, "--artifactsJson") || _argValue(argv, "--artifacts");
656
+ const outArg = _argValue(argv, "--out");
657
+ const nameArg = _argValue(argv, "--name");
658
+ const nonInteractive = _hasFlag(argv, "--non-interactive") || _hasFlag(argv, "--yes");
659
+ const createPackageFlag = _hasFlag(argv, "--create-package");
660
+ const pkgDirArg = _argValue(argv, "--package-dir");
661
+ const pkgNameArg = _argValue(argv, "--package-name");
662
+ const pkgDescArg = _argValue(argv, "--package-description") || "";
663
+ const pkgAuthorArg = _argValue(argv, "--package-author") || "";
664
+ const pkgLicenseArg = _argValue(argv, "--package-license") || "MIT";
665
+ const pkgVersionArg = _argValue(argv, "--package-version") || "0.0.1";
666
+ const lang = _normalizeLang(_argValue(argv, "--lang") || _argValue(argv, "--language") || _argValue(argv, "--type"));
667
+
668
+ // Decide input type.
669
+ let inputType = null; // "abibin" | "sol" | "artifactsjson"
670
+ if (solArg) inputType = "sol";
671
+ if (artifactsJsonArg) inputType = inputType || "artifactsjson";
672
+ if (abiPathArg || binPathArg) inputType = inputType || "abibin";
673
+ if ((solArg ? 1 : 0) + (artifactsJsonArg ? 1 : 0) + (abiPathArg || binPathArg ? 1 : 0) > 1) {
674
+ throw new Error("Select only one input mode: --abi/--bin OR --sol OR --artifacts-json.");
675
+ }
676
+
677
+ let createPackage = false;
678
+ let outDir = outArg ? path.resolve(outArg) : null;
679
+ let contractName = nameArg || null;
680
+
681
+ // Interactive prompt: ask input type first.
682
+ let absAbi = null;
683
+ let absBin = null;
684
+ let solFilesAbs = [];
685
+ let artifactsJsonAbs = null;
686
+ const solcExtraArgs = solcArgsRaw ? _parseSolcExtraArgs(solcArgsRaw) : [];
687
+
688
+ if (!nonInteractive) {
689
+ const rl0 = readline.createInterface({ input: stdin, output: stdout });
690
+ try {
691
+ if (!inputType) {
692
+ const ans = (await rl0.question("Input type? (1) ABI+BIN (2) SOL (3) ARTIFACTS JSON ")).trim();
693
+ inputType = ans === "2" ? "sol" : ans === "3" ? "artifactsjson" : "abibin";
694
+ }
695
+ if (inputType === "abibin") {
696
+ const abiP = abiPathArg || (await rl0.question("Enter ABI path (.json): ")).trim();
697
+ const binP = binPathArg || (await rl0.question("Enter bytecode path (.bin): ")).trim();
698
+ if (!abiP || !binP) throw new Error("Missing ABI/BIN paths.");
699
+ absAbi = path.resolve(abiP);
700
+ absBin = path.resolve(binP);
701
+ if (!fs.existsSync(absAbi)) throw new Error(`ABI file not found: ${absAbi}`);
702
+ if (!fs.existsSync(absBin)) throw new Error(`Bytecode file not found: ${absBin}`);
703
+ contractName = contractName || _cap(_defaultContractNameFromAbiPath(absAbi));
704
+ } else if (inputType === "sol") {
705
+ const solP = solArg || (await rl0.question("Enter Solidity file(s) (comma-separated): ")).trim();
706
+ const files = _parseCommaSeparatedFiles(solP);
707
+ if (files.length === 0) throw new Error("Missing Solidity file(s).");
708
+ solFilesAbs = files.map((p) => path.resolve(p));
709
+ for (const f of solFilesAbs) {
710
+ if (!fs.existsSync(f)) throw new Error(`Solidity file not found: ${f}`);
711
+ }
712
+ if (!contractName) {
713
+ const maybe = (await rl0.question("Contract name (optional; empty = generate all): ")).trim();
714
+ if (maybe) contractName = maybe;
715
+ }
716
+ } else {
717
+ const p = artifactsJsonArg || (await rl0.question("Enter artifacts JSON path (.json): ")).trim();
718
+ if (!p) throw new Error("Missing artifacts JSON path.");
719
+ artifactsJsonAbs = path.resolve(p);
720
+ if (!fs.existsSync(artifactsJsonAbs)) throw new Error(`Artifacts JSON file not found: ${artifactsJsonAbs}`);
721
+ }
722
+ } finally {
723
+ rl0.close();
724
+ }
725
+ }
726
+
727
+ // Non-interactive input validation
728
+ if (nonInteractive) {
729
+ if (!inputType) {
730
+ throw new Error("Select an input type: pass --abi/--bin OR pass --sol <file1.sol,file2.sol> OR pass --artifacts-json <file.json>.");
731
+ }
732
+ if (inputType === "abibin") {
733
+ if (!abiPathArg || !binPathArg) {
734
+ throw new Error("Missing required arguments: --abi <path> --bin <path>");
735
+ }
736
+ absAbi = path.resolve(abiPathArg);
737
+ absBin = path.resolve(binPathArg);
738
+ if (!fs.existsSync(absAbi)) throw new Error(`ABI file not found: ${absAbi}`);
739
+ if (!fs.existsSync(absBin)) throw new Error(`Bytecode file not found: ${absBin}`);
740
+ contractName = contractName || _cap(_defaultContractNameFromAbiPath(absAbi));
741
+ } else if (inputType === "sol") {
742
+ const files = _parseCommaSeparatedFiles(solArg);
743
+ if (files.length === 0) throw new Error("Missing required argument: --sol <file1.sol,file2.sol>");
744
+ solFilesAbs = files.map((p) => path.resolve(p));
745
+ for (const f of solFilesAbs) {
746
+ if (!fs.existsSync(f)) throw new Error(`Solidity file not found: ${f}`);
747
+ }
748
+ // contractName can be null => generate all
749
+ } else {
750
+ if (!artifactsJsonArg) throw new Error("Missing required argument: --artifacts-json <path/to/artifacts.json>");
751
+ artifactsJsonAbs = path.resolve(artifactsJsonArg);
752
+ if (!fs.existsSync(artifactsJsonAbs)) throw new Error(`Artifacts JSON file not found: ${artifactsJsonAbs}`);
753
+ }
754
+ }
755
+
756
+ if (createPackageFlag) {
757
+ createPackage = true;
758
+ }
759
+
760
+ if (!nonInteractive && !createPackageFlag) {
761
+ const rl = readline.createInterface({ input: stdin, output: stdout });
762
+ try {
763
+ const ans = (await rl.question("Do you want to create a new package? (Y/N) ")).trim().toLowerCase();
764
+ createPackage = ans === "y" || ans === "yes";
765
+
766
+ if (createPackage) {
767
+ const pkgFolder = (await rl.question("Enter the folder path where the package should be created: ")).trim();
768
+ const pkgName = (await rl.question("Enter package name: ")).trim();
769
+ const pkgDesc = (await rl.question("Enter package description: ")).trim();
770
+ const pkgAuthor = (await rl.question("Enter author name: ")).trim();
771
+ const pkgLicense = (await rl.question("Enter license (default: MIT): ")).trim() || "MIT";
772
+ const pkgVersion = (await rl.question("Enter version (default: 0.0.1): ")).trim() || "0.0.1";
773
+
774
+ outDir = path.resolve(pkgFolder, pkgName);
775
+ _ensureDir(outDir);
776
+
777
+ _createPackageScaffold({
778
+ outDir,
779
+ pkgName,
780
+ pkgDesc,
781
+ pkgAuthor,
782
+ pkgLicense,
783
+ pkgVersion,
784
+ lang,
785
+ });
786
+ } else {
787
+ const target = (await rl.question("Enter the location in your existing package (relative to package root): ")).trim();
788
+ outDir = path.resolve(process.cwd(), target || "src/contracts");
789
+ }
790
+ } finally {
791
+ rl.close();
792
+ }
793
+ }
794
+
795
+ if (createPackage && nonInteractive) {
796
+ const pkgFolder = pkgDirArg ? path.resolve(pkgDirArg) : null;
797
+ const pkgName = pkgNameArg || null;
798
+ if (!pkgFolder || !pkgName) {
799
+ throw new Error("When using --create-package in non-interactive mode, pass --package-dir <dir> and --package-name <name>.");
800
+ }
801
+ outDir = path.resolve(pkgFolder, pkgName);
802
+ _ensureDir(outDir);
803
+ _createPackageScaffold({
804
+ outDir,
805
+ pkgName,
806
+ pkgDesc: pkgDescArg,
807
+ pkgAuthor: pkgAuthorArg,
808
+ pkgLicense: pkgLicenseArg,
809
+ pkgVersion: pkgVersionArg,
810
+ lang,
811
+ });
812
+ }
813
+
814
+ if (!outDir) outDir = path.resolve(process.cwd(), "generated-contract");
815
+
816
+ const targetSrcDir = createPackage ? path.join(outDir, "src") : outDir;
817
+ _ensureDir(targetSrcDir);
818
+
819
+ // Resolve compilation / artifacts
820
+ /** @type {Array<{ contractName: string, abi: any[], bytecode: string }>} */
821
+ let artifacts = [];
822
+ if (inputType === "abibin") {
823
+ const abi = _readJson(absAbi);
824
+ const bytecodeRaw = fs.readFileSync(absBin, "utf8").trim();
825
+ const bytecode = bytecodeRaw.startsWith("0x") ? bytecodeRaw : `0x${bytecodeRaw}`;
826
+ artifacts = [{ contractName, abi, bytecode, docs: null }];
827
+ } else if (inputType === "sol") {
828
+ const solcPath = _resolveSolcPath(argv);
829
+ artifacts = _compileSolidityToArtifacts({ solcPath, solFilesAbs, contractNameFilter: contractName, solcExtraArgs });
830
+
831
+ // Attach Solidity doc comments (NatSpec) to artifacts
832
+ const docsByContract = _extractSolDocs(solFilesAbs);
833
+ for (const a of artifacts) {
834
+ a.docs = docsByContract[a.contractName] || null;
835
+ }
836
+
837
+ // As requested: emit ABI/BIN artifacts to disk
838
+ _writeSolcArtifacts(createPackage ? outDir : targetSrcDir, artifacts);
839
+ } else {
840
+ // Artifacts JSON
841
+ artifacts = _readArtifactsJson(artifactsJsonAbs || path.resolve(artifactsJsonArg));
842
+ }
843
+
844
+ if (artifacts.length === 1) {
845
+ // Keep the old API/behavior for single-contract generation.
846
+ const a = artifacts[0];
847
+ generateFromArtifacts({ outDir: targetSrcDir, artifacts: [a], lang });
848
+ } else {
849
+ generateFromArtifacts({ outDir: targetSrcDir, artifacts, lang });
850
+ }
851
+
852
+ if (createPackage) {
853
+ _writeText(
854
+ path.join(outDir, "README.md"),
855
+ _packageReadme({
856
+ pkgName: pkgNameArg || path.basename(outDir),
857
+ pkgDesc: pkgDescArg,
858
+ artifacts,
859
+ createdFromSolidity: inputType === "sol",
860
+ lang,
861
+ }),
862
+ );
863
+
864
+ // Transactional tests (always per-contract)
865
+ _ensureDir(path.join(outDir, "test", "e2e"));
866
+ _ensureDir(path.join(outDir, "examples"));
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
+
877
+ // TypeScript _test-wallet (for TS examples)
878
+ _writeText(
879
+ path.join(outDir, "examples", "_test-wallet.ts"),
880
+ `import { Wallet } from "quantumcoin";
881
+
882
+ // Hardcoded test wallet (test-only; never use for real funds)
883
+ export const TEST_WALLET_ENCRYPTED_JSON =
884
+ ${JSON.stringify(
885
+ "{\"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}",
886
+ )};
887
+ export const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
888
+
889
+ export function createTestWallet(provider: unknown) {
890
+ return Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
891
+ }
892
+ `,
893
+ );
894
+
895
+ for (const a of artifacts) {
896
+ _writeText(
897
+ path.join(outDir, "test", "e2e", `${a.contractName}.e2e.test.js`),
898
+ generateTransactionalTestJs({ contractName: a.contractName, abi: a.abi }),
899
+ );
900
+ }
901
+
902
+ if (artifacts.length > 1) {
903
+ _writeText(
904
+ path.join(outDir, "test", "e2e", "all-contracts.e2e.test.js"),
905
+ generateAllContractsTransactionalTestJs({ artifacts: artifacts.map((a) => ({ contractName: a.contractName, abi: a.abi })) }),
906
+ );
907
+ }
908
+
909
+ if (artifacts.length === 1) {
910
+ // Back-compat: keep original example filenames for a single-contract package.
911
+ const a = artifacts[0];
912
+ const ctor = _findConstructor(a.abi);
913
+ const ctorArgsExpr = (ctor.inputs || []).map((i) => _solTypeToExampleValueExpr(i)).join(", ");
914
+
915
+ _writeText(
916
+ path.join(outDir, "examples", "deploy.js"),
917
+ `/**
918
+ * Deploy example (generated).
919
+ *
920
+ * Requires:
921
+ * - QC_RPC_URL env var
922
+ *
923
+ * WARNING: uses a hardcoded test wallet (funded) for convenience.
924
+ */
925
+ const { Initialize } = require("quantumcoin/config");
926
+ const { getProvider, Wallet } = require("quantumcoin");
927
+ const { ${a.contractName}__factory } = require("..");
928
+
929
+ // Hardcoded test wallet (test-only; never use for real funds)
930
+ const TEST_WALLET_ENCRYPTED_JSON =
931
+ ${JSON.stringify(
932
+ "{\"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}",
933
+ )};
934
+ const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
935
+
936
+ async function main() {
937
+ const rpcUrl = process.env.QC_RPC_URL;
938
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
939
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
940
+ await Initialize(null);
941
+
942
+ const provider = getProvider(rpcUrl, chainId);
943
+ const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
944
+
945
+ const factory = new ${a.contractName}__factory(wallet);
946
+ const contract = await factory.deploy(${ctorArgsExpr}${ctorArgsExpr ? ", " : ""}{ gasLimit: 600000 });
947
+ const tx = contract.deployTransaction();
948
+ if (tx) await tx.wait(1, 600_000);
949
+
950
+ console.log("Deployed at:", contract.target);
951
+ console.log("Next:");
952
+ console.log(' $env:CONTRACT_ADDRESS="' + contract.target + '"');
953
+ console.log(" node examples/read-operations.js");
954
+ }
955
+
956
+ main().catch((e) => {
957
+ console.error(e);
958
+ process.exitCode = 1;
959
+ });
960
+ `,
961
+ );
962
+
963
+ _writeText(
964
+ path.join(outDir, "examples", "deploy.ts"),
965
+ `/**
966
+ * Deploy example (generated) - TypeScript.
967
+ *
968
+ * Requires:
969
+ * - QC_RPC_URL env var
970
+ *
971
+ * WARNING: uses a hardcoded test wallet (funded) for convenience.
972
+ */
973
+ import { Initialize } from "quantumcoin/config";
974
+ import { getProvider, Wallet } from "quantumcoin";
975
+ import { ${a.contractName}__factory } from "..";
976
+
977
+ // Hardcoded test wallet (test-only; never use for real funds)
978
+ const TEST_WALLET_ENCRYPTED_JSON =
979
+ ${JSON.stringify(
980
+ "{\"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}",
981
+ )};
982
+ const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
983
+
984
+ async function main(): Promise<void> {
985
+ const rpcUrl = process.env.QC_RPC_URL;
986
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
987
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
988
+ await Initialize(null);
989
+
990
+ const provider = getProvider(rpcUrl, chainId);
991
+ const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
992
+
993
+ const factory = new ${a.contractName}__factory(wallet);
994
+ const contract = await factory.deploy(${ctorArgsExpr}${ctorArgsExpr ? ", " : ""}{ gasLimit: 600000 });
995
+ const tx = contract.deployTransaction();
996
+ if (tx) await tx.wait(1, 600_000);
997
+
998
+ console.log("Deployed at:", contract.target);
999
+ console.log("Next:");
1000
+ console.log(' $env:CONTRACT_ADDRESS="' + contract.target + '"');
1001
+ console.log(" npx tsx examples/read-operations.ts");
1002
+ }
1003
+
1004
+ main().catch((e) => {
1005
+ console.error(e);
1006
+ process.exitCode = 1;
1007
+ });
1008
+ `,
1009
+ );
1010
+
1011
+ _writeText(
1012
+ path.join(outDir, "examples", "read-operations.js"),
1013
+ `/**
1014
+ * Read operations example (generated).
1015
+ *
1016
+ * Requires:
1017
+ * - QC_RPC_URL env var
1018
+ * - CONTRACT_ADDRESS env var
1019
+ */
1020
+ const { Initialize } = require("quantumcoin/config");
1021
+ const { getProvider } = require("quantumcoin");
1022
+ const { ${a.contractName} } = require("..");
1023
+
1024
+ async function main() {
1025
+ const rpcUrl = process.env.QC_RPC_URL;
1026
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
1027
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1028
+ const address = process.env.CONTRACT_ADDRESS;
1029
+ if (!address) throw new Error("CONTRACT_ADDRESS is required");
1030
+ await Initialize(null);
1031
+
1032
+ const provider = getProvider(rpcUrl, chainId);
1033
+ const contract = ${a.contractName}.connect(address, provider);
1034
+
1035
+ console.log("Contract:", contract.target);
1036
+
1037
+ if (typeof contract.name === "function") {
1038
+ console.log("name():", await contract.name());
1039
+ }
1040
+ if (typeof contract.symbol === "function") {
1041
+ console.log("symbol():", await contract.symbol());
1042
+ }
1043
+ if (typeof contract.totalSupply === "function") {
1044
+ // Generated wrappers already unwrap single-return values to a hard type.
1045
+ const v = await contract.totalSupply();
1046
+ console.log("totalSupply():", v.toString());
1047
+ }
1048
+ if (typeof contract.balanceOf === "function" && process.env.WALLET_ADDRESS) {
1049
+ // Generated wrappers already unwrap single-return values to a hard type.
1050
+ const v = await contract.balanceOf(process.env.WALLET_ADDRESS);
1051
+ console.log("balanceOf(WALLET_ADDRESS):", v.toString());
1052
+ }
1053
+ }
1054
+
1055
+ main().catch((e) => {
1056
+ console.error(e);
1057
+ process.exitCode = 1;
1058
+ });
1059
+ `,
1060
+ );
1061
+
1062
+ _writeText(
1063
+ path.join(outDir, "examples", "read-operations.ts"),
1064
+ `/**
1065
+ * Read operations example (generated) - TypeScript.
1066
+ *
1067
+ * Requires:
1068
+ * - QC_RPC_URL env var
1069
+ * - CONTRACT_ADDRESS env var
1070
+ */
1071
+ import { Initialize } from "quantumcoin/config";
1072
+ import { getProvider } from "quantumcoin";
1073
+ import { ${a.contractName} } from "..";
1074
+
1075
+ async function main(): Promise<void> {
1076
+ const rpcUrl = process.env.QC_RPC_URL;
1077
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
1078
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1079
+ const address = process.env.CONTRACT_ADDRESS;
1080
+ if (!address) throw new Error("CONTRACT_ADDRESS is required");
1081
+ await Initialize(null);
1082
+
1083
+ const provider = getProvider(rpcUrl, chainId);
1084
+ const contract = ${a.contractName}.connect(address, provider);
1085
+
1086
+ console.log("Contract:", contract.target);
1087
+
1088
+ if (typeof contract.name === "function") {
1089
+ console.log("name():", await contract.name());
1090
+ }
1091
+ if (typeof contract.symbol === "function") {
1092
+ console.log("symbol():", await contract.symbol());
1093
+ }
1094
+ if (typeof contract.totalSupply === "function") {
1095
+ const v = await contract.totalSupply();
1096
+ console.log("totalSupply():", v.toString());
1097
+ }
1098
+ if (typeof contract.balanceOf === "function" && process.env.WALLET_ADDRESS) {
1099
+ const v = await contract.balanceOf(process.env.WALLET_ADDRESS);
1100
+ console.log("balanceOf(WALLET_ADDRESS):", v.toString());
1101
+ }
1102
+ }
1103
+
1104
+ main().catch((e) => {
1105
+ console.error(e);
1106
+ process.exitCode = 1;
1107
+ });
1108
+ `,
1109
+ );
1110
+
1111
+ _writeText(
1112
+ path.join(outDir, "examples", "write-operations.js"),
1113
+ `/**
1114
+ * Write operations example (generated).
1115
+ *
1116
+ * Requires:
1117
+ * - QC_RPC_URL env var
1118
+ * - CONTRACT_ADDRESS env var
1119
+ *
1120
+ * WARNING: uses a hardcoded test wallet (funded) for convenience.
1121
+ */
1122
+ const { Initialize } = require("quantumcoin/config");
1123
+ const { getProvider, Wallet } = require("quantumcoin");
1124
+ const { ${a.contractName} } = require("..");
1125
+
1126
+ // Hardcoded test wallet (test-only; never use for real funds)
1127
+ const TEST_WALLET_ENCRYPTED_JSON =
1128
+ ${JSON.stringify(
1129
+ "{\"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}",
1130
+ )};
1131
+ const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
1132
+
1133
+ async function main() {
1134
+ const rpcUrl = process.env.QC_RPC_URL;
1135
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
1136
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1137
+ const address = process.env.CONTRACT_ADDRESS;
1138
+ if (!address) throw new Error("CONTRACT_ADDRESS is required");
1139
+ await Initialize(null);
1140
+
1141
+ const provider = getProvider(rpcUrl, chainId);
1142
+ const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
1143
+ const contract = ${a.contractName}.connect(address, wallet);
1144
+
1145
+ if (typeof contract.approve === "function") {
1146
+ const tx = await contract.approve(wallet.address, 123, { gasLimit: 200000 });
1147
+ await tx.wait(1, 600_000);
1148
+ console.log("approve(wallet.address, 123) succeeded");
1149
+ return;
1150
+ }
1151
+
1152
+ console.log("No known write method template for this ABI.");
1153
+ }
1154
+
1155
+ main().catch((e) => {
1156
+ console.error(e);
1157
+ process.exitCode = 1;
1158
+ });
1159
+ `,
1160
+ );
1161
+
1162
+ _writeText(
1163
+ path.join(outDir, "examples", "write-operations.ts"),
1164
+ `/**
1165
+ * Write operations example (generated) - TypeScript.
1166
+ *
1167
+ * Requires:
1168
+ * - QC_RPC_URL env var
1169
+ * - CONTRACT_ADDRESS env var
1170
+ *
1171
+ * WARNING: uses a hardcoded test wallet (funded) for convenience.
1172
+ */
1173
+ import { Initialize } from "quantumcoin/config";
1174
+ import { getProvider, Wallet } from "quantumcoin";
1175
+ import { ${a.contractName} } from "..";
1176
+
1177
+ // Hardcoded test wallet (test-only; never use for real funds)
1178
+ const TEST_WALLET_ENCRYPTED_JSON =
1179
+ ${JSON.stringify(
1180
+ "{\"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}",
1181
+ )};
1182
+ const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
1183
+
1184
+ async function main(): Promise<void> {
1185
+ const rpcUrl = process.env.QC_RPC_URL;
1186
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
1187
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1188
+ const address = process.env.CONTRACT_ADDRESS;
1189
+ if (!address) throw new Error("CONTRACT_ADDRESS is required");
1190
+ await Initialize(null);
1191
+
1192
+ const provider = getProvider(rpcUrl, chainId);
1193
+ const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
1194
+ const contract = ${a.contractName}.connect(address, wallet);
1195
+
1196
+ if (typeof contract.approve === "function") {
1197
+ const tx = await contract.approve(wallet.address, 123, { gasLimit: 200000 });
1198
+ await tx.wait(1, 600_000);
1199
+ console.log("approve(wallet.address, 123) succeeded");
1200
+ return;
1201
+ }
1202
+
1203
+ console.log("No known write method template for this ABI.");
1204
+ }
1205
+
1206
+ main().catch((e) => {
1207
+ console.error(e);
1208
+ process.exitCode = 1;
1209
+ });
1210
+ `,
1211
+ );
1212
+
1213
+ _writeText(
1214
+ path.join(outDir, "examples", "offline-signing.js"),
1215
+ `/**
1216
+ * Offline signing example (generated).
1217
+ *
1218
+ * Demonstrates:
1219
+ * - ${a.contractName}__factory.getDeployTransaction(...)
1220
+ * - contract.populateTransaction.<method>(...)
1221
+ * - wallet.signTransaction(txReq) (offline) + provider.sendRawTransaction(rawTx)
1222
+ *
1223
+ * Requires:
1224
+ * - QC_RPC_URL env var
1225
+ *
1226
+ * WARNING: uses a hardcoded test wallet (funded) for convenience.
1227
+ */
1228
+ const { Initialize } = require("quantumcoin/config");
1229
+ const { getProvider, Wallet, getCreateAddress } = require("quantumcoin");
1230
+ const { ${a.contractName}, ${a.contractName}__factory } = require("..");
1231
+
1232
+ // Hardcoded test wallet (test-only; never use for real funds)
1233
+ const TEST_WALLET_ENCRYPTED_JSON =
1234
+ ${JSON.stringify(
1235
+ "{\"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}",
1236
+ )};
1237
+ const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
1238
+
1239
+ async function main() {
1240
+ const rpcUrl = process.env.QC_RPC_URL;
1241
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
1242
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1243
+
1244
+ await Initialize(null);
1245
+ const provider = getProvider(rpcUrl, chainId);
1246
+
1247
+ // Offline wallet (no provider attached). We'll resolve nonce from provider manually.
1248
+ const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE);
1249
+ const from = wallet.address;
1250
+
1251
+ const factory = new ${a.contractName}__factory(wallet);
1252
+ const deployTxReq = factory.getDeployTransaction(${ctorArgsExpr});
1253
+
1254
+ let gasLimit = 600000;
1255
+ try {
1256
+ const est = await provider.estimateGas({ from, data: deployTxReq.data });
1257
+ gasLimit = Number(est + 200_000n);
1258
+ } catch {
1259
+ gasLimit = 6_000_000;
1260
+ }
1261
+ const bytecodeSize = (${a.contractName}.bytecode || "").length;
1262
+ if (bytecodeSize > 20000 && gasLimit < 6_000_000) gasLimit = 6_000_000;
1263
+
1264
+ const nonce0 = await provider.getTransactionCount(from, "pending");
1265
+ const predicted = getCreateAddress({ from, nonce: nonce0 });
1266
+
1267
+ const rawDeploy = await wallet.signTransaction({
1268
+ ...deployTxReq,
1269
+ nonce: nonce0,
1270
+ chainId,
1271
+ gasLimit,
1272
+ gasPrice: 1n,
1273
+ });
1274
+
1275
+ const sentDeploy = await provider.sendRawTransaction(rawDeploy);
1276
+ console.log("deploy tx hash:", sentDeploy.hash);
1277
+ await sentDeploy.wait(1, 600_000);
1278
+
1279
+ const contract = ${a.contractName}.connect(predicted, provider);
1280
+ console.log("deployed at:", contract.target);
1281
+
1282
+ // Optional: offline-sign a write method if present (ERC20-like approve)
1283
+ if (contract.populateTransaction && typeof contract.populateTransaction.approve === "function") {
1284
+ const txReq = await contract.populateTransaction.approve(from, 123, { gasLimit: 200000 });
1285
+ const nonce1 = await provider.getTransactionCount(from, "pending");
1286
+ const raw = await wallet.signTransaction({ ...txReq, nonce: nonce1, chainId, gasPrice: 1n });
1287
+ const sent = await provider.sendRawTransaction(raw);
1288
+ console.log("approve tx hash:", sent.hash);
1289
+ await sent.wait(1, 600_000);
1290
+ console.log("approve succeeded");
1291
+ } else {
1292
+ console.log("No known write method for offline-signing demo (skipping write tx).");
1293
+ }
1294
+ }
1295
+
1296
+ main().catch((e) => {
1297
+ console.error(e);
1298
+ process.exitCode = 1;
1299
+ });
1300
+ `,
1301
+ );
1302
+
1303
+ _writeText(
1304
+ path.join(outDir, "examples", "offline-signing.ts"),
1305
+ `/**
1306
+ * Offline signing example (generated) - TypeScript.
1307
+ *
1308
+ * Demonstrates:
1309
+ * - ${a.contractName}__factory.getDeployTransaction(...)
1310
+ * - contract.populateTransaction.<method>(...)
1311
+ * - wallet.signTransaction(txReq) (offline) + provider.sendRawTransaction(rawTx)
1312
+ *
1313
+ * Requires:
1314
+ * - QC_RPC_URL env var
1315
+ *
1316
+ * WARNING: uses a hardcoded test wallet (funded) for convenience.
1317
+ */
1318
+ import { Initialize } from "quantumcoin/config";
1319
+ import { getProvider, Wallet, getCreateAddress } from "quantumcoin";
1320
+ import { ${a.contractName}, ${a.contractName}__factory } from "..";
1321
+
1322
+ // Hardcoded test wallet (test-only; never use for real funds)
1323
+ const TEST_WALLET_ENCRYPTED_JSON =
1324
+ ${JSON.stringify(
1325
+ "{\"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}",
1326
+ )};
1327
+ const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
1328
+
1329
+ async function main(): Promise<void> {
1330
+ const rpcUrl = process.env.QC_RPC_URL;
1331
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
1332
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1333
+
1334
+ await Initialize(null);
1335
+ const provider = getProvider(rpcUrl, chainId);
1336
+
1337
+ const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE);
1338
+ const from = wallet.address;
1339
+
1340
+ const factory = new ${a.contractName}__factory(wallet);
1341
+ const deployTxReq = factory.getDeployTransaction(${ctorArgsExpr});
1342
+
1343
+ let gasLimit = 600000;
1344
+ try {
1345
+ const est = await provider.estimateGas({ from, data: deployTxReq.data });
1346
+ gasLimit = Number(est + 200_000n);
1347
+ } catch {
1348
+ gasLimit = 6_000_000;
1349
+ }
1350
+ const bytecodeSize = (${a.contractName}.bytecode || "").length;
1351
+ if (bytecodeSize > 20000 && gasLimit < 6_000_000) gasLimit = 6_000_000;
1352
+
1353
+ const nonce0 = await provider.getTransactionCount(from, "pending");
1354
+ const predicted = getCreateAddress({ from, nonce: nonce0 });
1355
+
1356
+ const rawDeploy = await wallet.signTransaction({
1357
+ ...deployTxReq,
1358
+ nonce: nonce0,
1359
+ chainId,
1360
+ gasLimit,
1361
+ gasPrice: 1n,
1362
+ });
1363
+
1364
+ const sentDeploy = await provider.sendRawTransaction(rawDeploy);
1365
+ console.log("deploy tx hash:", sentDeploy.hash);
1366
+ await sentDeploy.wait(1, 600_000);
1367
+
1368
+ const contract = ${a.contractName}.connect(predicted, provider);
1369
+ console.log("deployed at:", contract.target);
1370
+
1371
+ if (contract.populateTransaction && typeof contract.populateTransaction.approve === "function") {
1372
+ const txReq = await contract.populateTransaction.approve(from, 123, { gasLimit: 200000 });
1373
+ const nonce1 = await provider.getTransactionCount(from, "pending");
1374
+ const raw = await wallet.signTransaction({ ...txReq, nonce: nonce1, chainId, gasPrice: 1n });
1375
+ const sent = await provider.sendRawTransaction(raw);
1376
+ console.log("approve tx hash:", sent.hash);
1377
+ await sent.wait(1, 600_000);
1378
+ console.log("approve succeeded");
1379
+ } else {
1380
+ console.log("No known write method for offline-signing demo (skipping write tx).");
1381
+ }
1382
+ }
1383
+
1384
+ main().catch((e) => {
1385
+ console.error(e);
1386
+ process.exitCode = 1;
1387
+ });
1388
+ `,
1389
+ );
1390
+
1391
+ _writeText(
1392
+ path.join(outDir, "examples", "events.js"),
1393
+ `/**
1394
+ * Events/logs example (generated).
1395
+ *
1396
+ * Requires:
1397
+ * - QC_RPC_URL env var
1398
+ * - CONTRACT_ADDRESS env var
1399
+ */
1400
+ const { Initialize } = require("quantumcoin/config");
1401
+ const { getProvider } = require("quantumcoin");
1402
+ const { ${a.contractName} } = require("..");
1403
+
1404
+ async function main() {
1405
+ const rpcUrl = process.env.QC_RPC_URL;
1406
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
1407
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1408
+ const address = process.env.CONTRACT_ADDRESS;
1409
+ if (!address) throw new Error("CONTRACT_ADDRESS is required");
1410
+ await Initialize(null);
1411
+
1412
+ const provider = getProvider(rpcUrl, chainId);
1413
+ const contract = ${a.contractName}.connect(address, provider);
1414
+
1415
+ const fromBlock = process.env.FROM_BLOCK ? Number(process.env.FROM_BLOCK) : "latest";
1416
+ const toBlock = process.env.TO_BLOCK ? Number(process.env.TO_BLOCK) : "latest";
1417
+
1418
+ const logs = await contract.queryFilter("Transfer", fromBlock, toBlock);
1419
+ console.log("Logs:", logs.length);
1420
+ for (const l of logs.slice(0, 10)) {
1421
+ console.log(l);
1422
+ }
1423
+ }
1424
+
1425
+ main().catch((e) => {
1426
+ console.error(e);
1427
+ process.exitCode = 1;
1428
+ });
1429
+ `,
1430
+ );
1431
+
1432
+ _writeText(
1433
+ path.join(outDir, "examples", "events.ts"),
1434
+ `/**
1435
+ * Events/logs example (generated) - TypeScript.
1436
+ *
1437
+ * Requires:
1438
+ * - QC_RPC_URL env var
1439
+ * - CONTRACT_ADDRESS env var
1440
+ */
1441
+ import { Initialize } from "quantumcoin/config";
1442
+ import { getProvider } from "quantumcoin";
1443
+ import { ${a.contractName} } from "..";
1444
+
1445
+ async function main(): Promise<void> {
1446
+ const rpcUrl = process.env.QC_RPC_URL;
1447
+ if (!rpcUrl) throw new Error("QC_RPC_URL is required");
1448
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1449
+ const address = process.env.CONTRACT_ADDRESS;
1450
+ if (!address) throw new Error("CONTRACT_ADDRESS is required");
1451
+ await Initialize(null);
1452
+
1453
+ const provider = getProvider(rpcUrl, chainId);
1454
+ const contract = ${a.contractName}.connect(address, provider);
1455
+
1456
+ const fromBlock = process.env.FROM_BLOCK ? Number(process.env.FROM_BLOCK) : "latest";
1457
+ const toBlock = process.env.TO_BLOCK ? Number(process.env.TO_BLOCK) : "latest";
1458
+
1459
+ const logs = await contract.queryFilter("Transfer", fromBlock, toBlock);
1460
+ console.log("Logs:", logs.length);
1461
+ for (const l of logs.slice(0, 10)) {
1462
+ console.log(l);
1463
+ }
1464
+ }
1465
+
1466
+ main().catch((e) => {
1467
+ console.error(e);
1468
+ process.exitCode = 1;
1469
+ });
1470
+ `,
1471
+ );
1472
+ } else {
1473
+ // Multi-contract: avoid filename collisions.
1474
+ for (const a of artifacts) {
1475
+ const ctor = _findConstructor(a.abi);
1476
+ const ctorArgsExpr = (ctor.inputs || []).map((i) => _solTypeToExampleValueExpr(i)).join(", ");
1477
+
1478
+ _writeText(
1479
+ path.join(outDir, "examples", `deploy-${a.contractName}.js`),
1480
+ `const { Initialize } = require("quantumcoin/config");\nconst { getProvider } = 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 = getProvider(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`,
1481
+ );
1482
+
1483
+ _writeText(
1484
+ path.join(outDir, "examples", `read-operations-${a.contractName}.js`),
1485
+ `const { Initialize } = require("quantumcoin/config");\nconst { getProvider } = 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 = getProvider(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`,
1486
+ );
1487
+
1488
+ _writeText(
1489
+ path.join(outDir, "examples", `write-operations-${a.contractName}.js`),
1490
+ `const { Initialize } = require("quantumcoin/config");\nconst { getProvider } = 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 = getProvider(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`,
1491
+ );
1492
+
1493
+ _writeText(
1494
+ path.join(outDir, "examples", `offline-signing-${a.contractName}.js`),
1495
+ `const { Initialize } = require("quantumcoin/config");\nconst { getProvider, 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 = getProvider(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`,
1496
+ );
1497
+
1498
+ _writeText(
1499
+ path.join(outDir, "examples", `events-${a.contractName}.js`),
1500
+ `const { Initialize } = require("quantumcoin/config");\nconst { getProvider } = 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 = getProvider(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`,
1501
+ );
1502
+
1503
+ _writeText(
1504
+ path.join(outDir, "examples", `deploy-${a.contractName}.ts`),
1505
+ `import { Initialize } from "quantumcoin/config";\nimport { getProvider } from "quantumcoin";\nimport { createTestWallet } from "./_test-wallet";\nimport { ${a.contractName}__factory } from "..";\n\nasync function main(): Promise<void> {\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 = getProvider(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`,
1506
+ );
1507
+ _writeText(
1508
+ path.join(outDir, "examples", `read-operations-${a.contractName}.ts`),
1509
+ `import { Initialize } from "quantumcoin/config";\nimport { getProvider } from "quantumcoin";\nimport { ${a.contractName} } from "..";\n\nasync function main(): Promise<void> {\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 = getProvider(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`,
1510
+ );
1511
+ _writeText(
1512
+ path.join(outDir, "examples", `write-operations-${a.contractName}.ts`),
1513
+ `import { Initialize } from "quantumcoin/config";\nimport { getProvider } from "quantumcoin";\nimport { createTestWallet } from "./_test-wallet";\nimport { ${a.contractName} } from "..";\n\nasync function main(): Promise<void> {\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 = getProvider(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`,
1514
+ );
1515
+ _writeText(
1516
+ path.join(outDir, "examples", `offline-signing-${a.contractName}.ts`),
1517
+ `import { Initialize } from "quantumcoin/config";\nimport { getProvider, Wallet, getCreateAddress } from "quantumcoin";\nimport { TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE } from "./_test-wallet";\nimport { ${a.contractName}__factory, ${a.contractName} } from "..";\n\nasync function main(): Promise<void> {\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 = getProvider(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`,
1518
+ );
1519
+ _writeText(
1520
+ path.join(outDir, "examples", `events-${a.contractName}.ts`),
1521
+ `import { Initialize } from "quantumcoin/config";\nimport { getProvider } from "quantumcoin";\nimport { ${a.contractName} } from "..";\n\nasync function main(): Promise<void> {\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 = getProvider(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`,
1522
+ );
1523
+ }
1524
+ }
1525
+
1526
+ // Add examples scripts and tsx devDependency so TS examples can run
1527
+ const pkgPath = path.join(outDir, "package.json");
1528
+ const pkg = _readJson(pkgPath);
1529
+ pkg.scripts = pkg.scripts || {};
1530
+ if (artifacts.length === 1) {
1531
+ pkg.scripts.examples = "node examples/deploy.js && node examples/read-operations.js && node examples/write-operations.js && node examples/offline-signing.js && node examples/events.js";
1532
+ pkg.scripts["examples:ts"] = "npx tsx examples/deploy.ts && npx tsx examples/read-operations.ts && npx tsx examples/write-operations.ts && npx tsx examples/offline-signing.ts && npx tsx examples/events.ts";
1533
+ } else {
1534
+ const tsParts = [];
1535
+ for (const a of artifacts) {
1536
+ tsParts.push(`npx tsx examples/deploy-${a.contractName}.ts`, `npx tsx examples/read-operations-${a.contractName}.ts`, `npx tsx examples/write-operations-${a.contractName}.ts`, `npx tsx examples/offline-signing-${a.contractName}.ts`, `npx tsx examples/events-${a.contractName}.ts`);
1537
+ }
1538
+ pkg.scripts["examples:ts"] = tsParts.join(" && ");
1539
+ }
1540
+ pkg.devDependencies = pkg.devDependencies || {};
1541
+ pkg.devDependencies.tsx = "^4.7.0";
1542
+ _writeJson(pkgPath, pkg);
1543
+
1544
+ // Write an index.js re-export shim, then install and run build scripts.
1545
+ _writeText(path.join(outDir, "index.js"), _renderPackageIndexJs({ artifacts, entryDir: lang === "ts" ? "dist" : "src" }));
1546
+ }
1547
+
1548
+ // Final step: after package creation, emit types.
1549
+ if (createPackage) {
1550
+ _runNpm(["install", "--no-fund", "--no-audit"], outDir);
1551
+ if (lang === "ts") {
1552
+ _runNpm(["run", "build:ts"], outDir);
1553
+ }
1554
+ }
1555
+
1556
+ console.warn("This is an experimental SDK. Use at your own risk.");
1557
+ console.log(`Generated contract files in: ${targetSrcDir}`);
1558
+ }
1559
+
1560
+ function _cap(s) {
1561
+ return s ? s[0].toUpperCase() + s.slice(1) : s;
1562
+ }
1563
+
1564
+ function _abiParamSig(p, { includeName = true } = {}) {
1565
+ if (!p || typeof p !== "object") return "";
1566
+ const t = typeof p.type === "string" ? p.type : "";
1567
+ const n = includeName && typeof p.name === "string" && p.name ? ` ${p.name}` : "";
1568
+ const indexed = p.indexed ? " indexed" : "";
1569
+ return `${t}${indexed}${n}`.trim();
1570
+ }
1571
+
1572
+ function _abiFnSig(f) {
1573
+ const inputs = (f.inputs || []).map((p) => _abiParamSig(p)).filter(Boolean).join(", ");
1574
+ const outputs = (f.outputs || []).map((p) => _abiParamSig(p, { includeName: false })).filter(Boolean).join(", ");
1575
+ const mut = f.stateMutability && f.stateMutability !== "nonpayable" ? ` ${f.stateMutability}` : "";
1576
+ const returns = outputs ? ` returns (${outputs})` : "";
1577
+ return `${f.name}(${inputs})${mut}${returns}`.trim();
1578
+ }
1579
+
1580
+ function _abiEventSig(e) {
1581
+ const inputs = (e.inputs || []).map((p) => _abiParamSig(p)).filter(Boolean).join(", ");
1582
+ return `${e.name}(${inputs})`.trim();
1583
+ }
1584
+
1585
+ function _abiErrorSig(er) {
1586
+ const inputs = (er.inputs || []).map((p) => _abiParamSig(p)).filter(Boolean).join(", ");
1587
+ return `${er.name}(${inputs})`.trim();
1588
+ }
1589
+
1590
+ function _firstLine(s) {
1591
+ if (!s) return "";
1592
+ const t = String(s).trim();
1593
+ if (!t) return "";
1594
+ return t.split(/\r?\n/g)[0].trim();
1595
+ }
1596
+
1597
+ function _packageReadme({ pkgName, pkgDesc, artifacts, createdFromSolidity, lang = "ts" }) {
1598
+ const outLang = _normalizeLang(lang);
1599
+ const srcExt = outLang === "js" ? "js" : "ts";
1600
+ const list = (artifacts || []).map((a) => a.contractName).filter(Boolean);
1601
+ const hasMultiple = list.length > 1;
1602
+
1603
+ const contractLinks = list.length
1604
+ ? list.map((n) => `- [\`${n}\`](#${n.toLowerCase()})`).join("\n")
1605
+ : "- (none)";
1606
+
1607
+ const envBlock = `- \`QC_RPC_URL\` (required for transactional tests)\n- \`QC_CHAIN_ID\` (optional; defaults are used if omitted)\n`;
1608
+
1609
+ const commonExamples = hasMultiple
1610
+ ? `Examples are generated per contract (e.g. \`examples/deploy-<Contract>.js\`).`
1611
+ : `- [deploy](./examples/deploy.js)\n- [read operations](./examples/read-operations.js)\n- [write operations](./examples/write-operations.js)\n- [events](./examples/events.js)\n`;
1612
+
1613
+ const contractsMd = (artifacts || [])
1614
+ .map((a) => {
1615
+ const name = a.contractName;
1616
+ const desc = a.docs && typeof a.docs.contract === "string" && a.docs.contract.trim() ? a.docs.contract.trim() : "";
1617
+
1618
+ const fnDocs = (a.docs && a.docs.functions) || {};
1619
+ const abi = Array.isArray(a.abi) ? a.abi : [];
1620
+ const functions = abi.filter((x) => x && x.type === "function").sort((x, y) => String(x.name).localeCompare(String(y.name)));
1621
+ const events = abi.filter((x) => x && x.type === "event").sort((x, y) => String(x.name).localeCompare(String(y.name)));
1622
+ const errors = abi.filter((x) => x && x.type === "error").sort((x, y) => String(x.name).localeCompare(String(y.name)));
1623
+ const ctor = abi.find((x) => x && x.type === "constructor");
1624
+
1625
+ const examples = hasMultiple
1626
+ ? `- [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`
1627
+ : `- [deploy](./examples/deploy.js)\n- [read operations](./examples/read-operations.js)\n- [write operations](./examples/write-operations.js)\n- [events](./examples/events.js)\n`;
1628
+
1629
+ const testLink = `- [transactional test](./test/e2e/${name}.e2e.test.js)\n`;
1630
+
1631
+ const fileLinks = [
1632
+ `- [\`src/${name}.${srcExt}\`](./src/${name}.${srcExt})`,
1633
+ `- [\`src/${name}__factory.${srcExt}\`](./src/${name}__factory.${srcExt})`,
1634
+ createdFromSolidity ? `- [\`artifacts/${name}.abi.json\`](./artifacts/${name}.abi.json)` : null,
1635
+ createdFromSolidity ? `- [\`artifacts/${name}.bin\`](./artifacts/${name}.bin)` : null,
1636
+ ]
1637
+ .filter(Boolean)
1638
+ .join("\n");
1639
+
1640
+ const ctorSig = ctor
1641
+ ? `\`constructor(${(ctor.inputs || []).map((p) => _abiParamSig(p)).filter(Boolean).join(", ")})\``
1642
+ : "`constructor()`";
1643
+
1644
+ const fnList = functions.length
1645
+ ? functions
1646
+ .map((f) => {
1647
+ const doc = fnDocs && typeof fnDocs[f.name] === "string" ? _firstLine(fnDocs[f.name]) : "";
1648
+ return `- \`${_abiFnSig(f)}\`${doc ? ` — ${doc}` : ""}`;
1649
+ })
1650
+ .join("\n")
1651
+ : "- (none)";
1652
+
1653
+ const eventList = events.length ? events.map((e) => `- \`${_abiEventSig(e)}\``).join("\n") : "- (none)";
1654
+ const errorList = errors.length ? errors.map((er) => `- \`${_abiErrorSig(er)}\``).join("\n") : "- (none)";
1655
+
1656
+ return [
1657
+ `## ${name}`,
1658
+ desc ? `\n${desc}\n` : "",
1659
+ `- **Exports**: \`${name}\`, \`${name}__factory\``,
1660
+ `- **Constructor**: ${ctorSig}`,
1661
+ "",
1662
+ "### Files",
1663
+ fileLinks,
1664
+ "",
1665
+ "### Examples",
1666
+ examples.trimEnd(),
1667
+ "",
1668
+ "### Tests",
1669
+ testLink.trimEnd(),
1670
+ "",
1671
+ "### Functions",
1672
+ fnList,
1673
+ "",
1674
+ "### Events",
1675
+ eventList,
1676
+ "",
1677
+ "### Errors",
1678
+ errorList,
1679
+ "",
1680
+ ]
1681
+ .filter(Boolean)
1682
+ .join("\n");
1683
+ })
1684
+ .join("\n");
1685
+
1686
+ return (
1687
+ `# ${pkgName}\n\n` +
1688
+ `${pkgDesc || ""}\n\n` +
1689
+ "> **Note:** This is an experimental SDK. Use at your own risk.\n\n" +
1690
+ "## What’s in this package\n\n" +
1691
+ (outLang === "ts"
1692
+ ? "- Typed contract wrappers and factories in `src/` (compiled output in `dist/`)\n"
1693
+ : "- JavaScript contract wrappers and factories in `src/` (TypeScript types via `.d.ts`)\n") +
1694
+ "- Transactional tests in `test/e2e/`\n" +
1695
+ `- Example scripts in \`examples/\`\n` +
1696
+ (createdFromSolidity ? "- ABI/BIN artifacts in `artifacts/`\n" : "") +
1697
+ "\n" +
1698
+ "## Install\n\n" +
1699
+ "- `npm install`\n\n" +
1700
+ "## Build\n\n" +
1701
+ (outLang === "ts" ? "- `npm run build:ts`\n\n" : "- (no build step required)\n\n") +
1702
+ "## Run tests\n\n" +
1703
+ "- `npm test`\n\n" +
1704
+ "Transactional tests require:\n" +
1705
+ envBlock +
1706
+ "\n" +
1707
+ "## Examples\n\n" +
1708
+ (hasMultiple ? `${commonExamples}\n\n` : `${commonExamples}\n`) +
1709
+ "## Contracts\n\n" +
1710
+ contractLinks +
1711
+ "\n\n" +
1712
+ (contractsMd ? contractsMd : "") +
1713
+ "\n"
1714
+ );
1715
+ }
1716
+
1717
+ function _createPackageScaffold({ outDir, pkgName, pkgDesc, pkgAuthor, pkgLicense, pkgVersion, lang = "ts" }) {
1718
+ _ensureDir(outDir);
1719
+ _ensureDir(path.join(outDir, "src"));
1720
+ _ensureDir(path.join(outDir, "test", "e2e"));
1721
+ _ensureDir(path.join(outDir, "examples"));
1722
+
1723
+ const outLang = _normalizeLang(lang);
1724
+ const isTs = outLang === "ts";
1725
+
1726
+ const rootPkg = _readRootPackageJson();
1727
+ const rootDeps = _rewriteFileDepsToAbsolute(rootPkg.dependencies || {}, __dirname);
1728
+
1729
+ // Ensure the generated package depends on this repo's quantumcoin via absolute file path.
1730
+ rootDeps.quantumcoin = `file:${__dirname.replace(/\\\\/g, "/")}`;
1731
+
1732
+ const pkgJson = {
1733
+ name: pkgName,
1734
+ version: pkgVersion,
1735
+ description: pkgDesc,
1736
+ author: pkgAuthor,
1737
+ license: pkgLicense,
1738
+ main: isTs ? "dist/index.js" : "src/index.js",
1739
+ types: isTs ? "dist/index.d.ts" : "src/index.d.ts",
1740
+ scripts: {
1741
+ ...(isTs
1742
+ ? {
1743
+ "build:ts": "npx -p typescript tsc -p tsconfig.json",
1744
+ build: "npm run build:ts",
1745
+ "build-powershell": "npm run build:ts",
1746
+ test: "npm run build:ts && node --test --test-concurrency=1 \"test/**/*.test.js\"",
1747
+ "test:e2e": "npm run build:ts && node --test --test-concurrency=1 \"test/e2e/**/*.test.js\"",
1748
+ }
1749
+ : {
1750
+ build: "node -e \"console.log('JS package: no build step required')\"",
1751
+ "build-powershell": "node -e \"console.log('JS package: no build step required')\"",
1752
+ test: "node --test --test-concurrency=1 \"test/**/*.test.js\"",
1753
+ "test:e2e": "node --test --test-concurrency=1 \"test/e2e/**/*.test.js\"",
1754
+ }),
1755
+ },
1756
+ dependencies: rootDeps,
1757
+ devDependencies: {},
1758
+ };
1759
+
1760
+ _writeJson(path.join(outDir, "package.json"), pkgJson);
1761
+ if (isTs) {
1762
+ _writeJson(path.join(outDir, "tsconfig.json"), {
1763
+ compilerOptions: {
1764
+ target: "ES2022",
1765
+ lib: ["ES2022"],
1766
+ module: "CommonJS",
1767
+ declaration: true,
1768
+ outDir: "dist",
1769
+ strict: true,
1770
+ esModuleInterop: true,
1771
+ skipLibCheck: true,
1772
+ },
1773
+ include: ["src/**/*.ts"],
1774
+ });
1775
+ }
1776
+
1777
+ _writeText(
1778
+ path.join(outDir, "README.md"),
1779
+ _packageReadme({ pkgName, pkgDesc, artifacts: [], createdFromSolidity: false, lang: outLang }),
1780
+ );
1781
+
1782
+ _writeText(path.join(outDir, ".gitignore"), `node_modules\n/dist\n*.log\n`);
1783
+
1784
+ // Provide a root index.d.ts without needing a separate build step.
1785
+ // This is mainly for convenience and for tooling that expects a top-level .d.ts.
1786
+ _writeText(path.join(outDir, "index.d.ts"), `export * from "./${isTs ? "dist" : "src"}";\n`);
1787
+
1788
+ // Minimal shims so the generated TypeScript can compile even though `quantumcoin`
1789
+ // is a JavaScript package (no bundled .d.ts in this repo).
1790
+ _writeText(
1791
+ path.join(outDir, "src", "quantumcoin-shims.d.ts"),
1792
+ `declare module "quantumcoin" {\n` +
1793
+ ` export type ContractRunner = any;\n` +
1794
+ ` export type TransactionResponse = any;\n` +
1795
+ ` export type ContractTransactionResponse = any;\n` +
1796
+ ` export type TransactionRequest = any;\n` +
1797
+ `\n` +
1798
+ ` export class Contract {\n` +
1799
+ ` constructor(address: string, abi: any, runner?: any, bytecode?: any);\n` +
1800
+ ` target: string;\n` +
1801
+ ` address: string;\n` +
1802
+ ` interface: any;\n` +
1803
+ ` populateTransaction: any;\n` +
1804
+ ` call(methodName: string, args: any[], overrides?: TransactionRequest): Promise<any>;\n` +
1805
+ ` send(methodName: string, args: any[], overrides?: TransactionRequest): Promise<ContractTransactionResponse>;\n` +
1806
+ ` deployTransaction(): TransactionResponse | null;\n` +
1807
+ ` }\n` +
1808
+ `\n` +
1809
+ ` export class ContractFactory {\n` +
1810
+ ` signer: any;\n` +
1811
+ ` constructor(abi: any, bytecode: string, signer: any);\n` +
1812
+ ` getDeployTransaction(...args: any[]): TransactionRequest;\n` +
1813
+ ` }\n` +
1814
+ `\n` +
1815
+ ` export function getCreateAddress(opts: { from: string; nonce: number }): string;\n` +
1816
+ `}\n`,
1817
+ );
1818
+ }
1819
+
1820
+ main().catch((e) => {
1821
+ console.error(e);
1822
+ process.exitCode = 1;
1823
+ });
1824
+