quantum-coin-js-sdk 1.0.39 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/.gitignore +2 -1
  2. package/README.md +109 -768
  3. package/index.d.ts +76 -545
  4. package/index.js +319 -1194
  5. package/package.json +20 -5
  6. package/wasmBase64.js +7 -16
  7. package/wasm_exec.js +575 -587
  8. package/.idea/.name +0 -1
  9. package/.idea/modules.xml +0 -8
  10. package/.idea/quantum-coin-js-sdk.iml +0 -12
  11. package/.idea/vcs.xml +0 -6
  12. package/.vs/ProjectSettings.json +0 -3
  13. package/.vs/VSWorkspaceState.json +0 -8
  14. package/.vs/quantum-coin-js-sdk/FileContentIndex/a9d6de82-cfc3-4c22-ac38-06d706ff3673.vsidx +0 -0
  15. package/.vs/quantum-coin-js-sdk/FileContentIndex/fdc816df-1898-4153-99f2-c69ef4c2283b.vsidx +0 -0
  16. package/.vs/quantum-coin-js-sdk/config/applicationhost.config +0 -1011
  17. package/.vs/quantum-coin-js-sdk/v17/.wsuo +0 -0
  18. package/.vs/quantum-coin-js-sdk/v17/DocumentLayout.backup.json +0 -39
  19. package/.vs/quantum-coin-js-sdk/v17/DocumentLayout.json +0 -57
  20. package/.vs/slnx.sqlite +0 -0
  21. package/example/README.md +0 -14
  22. package/example/conversion-example.js +0 -19
  23. package/example/example-create-contract.js +0 -396
  24. package/example/example-encode-decode-rlp.js +0 -225
  25. package/example/example-event-pack-unpack.js +0 -391
  26. package/example/example-misc.js +0 -100
  27. package/example/example-rpc-send-signRawTransaction.js +0 -318
  28. package/example/example-rpc-send.js +0 -115
  29. package/example/example-send.js +0 -69
  30. package/example/example-token-pack-unpack.js +0 -960
  31. package/example/example-wallet-version4.js +0 -34
  32. package/example/example-wallet.js +0 -43
  33. package/example/example.js +0 -405
  34. package/example/node_modules/.bin/sdkgen +0 -16
  35. package/example/node_modules/.bin/sdkgen.cmd +0 -17
  36. package/example/node_modules/.bin/sdkgen.ps1 +0 -28
  37. package/example/node_modules/.package-lock.json +0 -47
  38. package/example/node_modules/quantumcoin/.github/workflows/publish-npmjs.yaml +0 -22
  39. package/example/node_modules/quantumcoin/LICENSE +0 -21
  40. package/example/node_modules/quantumcoin/README-SDK.md +0 -828
  41. package/example/node_modules/quantumcoin/README.md +0 -197
  42. package/example/node_modules/quantumcoin/config.d.ts +0 -50
  43. package/example/node_modules/quantumcoin/config.js +0 -123
  44. package/example/node_modules/quantumcoin/examples/AllSolidityTypes.sol +0 -184
  45. package/example/node_modules/quantumcoin/examples/SimpleIERC20.sol +0 -74
  46. package/example/node_modules/quantumcoin/examples/events.js +0 -41
  47. package/example/node_modules/quantumcoin/examples/events.ts +0 -35
  48. package/example/node_modules/quantumcoin/examples/example-generator-sdk-js.js +0 -100
  49. package/example/node_modules/quantumcoin/examples/example-generator-sdk-js.ts +0 -77
  50. package/example/node_modules/quantumcoin/examples/example-generator-sdk-ts.js +0 -100
  51. package/example/node_modules/quantumcoin/examples/example-generator-sdk-ts.ts +0 -77
  52. package/example/node_modules/quantumcoin/examples/example.js +0 -67
  53. package/example/node_modules/quantumcoin/examples/example.ts +0 -56
  54. package/example/node_modules/quantumcoin/examples/offline-signing.js +0 -77
  55. package/example/node_modules/quantumcoin/examples/offline-signing.ts +0 -65
  56. package/example/node_modules/quantumcoin/examples/package-lock.json +0 -596
  57. package/example/node_modules/quantumcoin/examples/package.json +0 -32
  58. package/example/node_modules/quantumcoin/examples/read-operations.js +0 -32
  59. package/example/node_modules/quantumcoin/examples/read-operations.ts +0 -31
  60. package/example/node_modules/quantumcoin/examples/sdk-generator-erc20.inline.json +0 -251
  61. package/example/node_modules/quantumcoin/examples/solidity-types.ts +0 -43
  62. package/example/node_modules/quantumcoin/examples/wallet-offline.js +0 -36
  63. package/example/node_modules/quantumcoin/examples/wallet-offline.ts +0 -26
  64. package/example/node_modules/quantumcoin/generate-sdk.js +0 -1846
  65. package/example/node_modules/quantumcoin/index.js +0 -12
  66. package/example/node_modules/quantumcoin/package.json +0 -95
  67. package/example/node_modules/quantumcoin/scripts/copy-declarations.js +0 -31
  68. package/example/node_modules/quantumcoin/scripts/run-all-one-by-one.js +0 -151
  69. package/example/node_modules/quantumcoin/src/abi/fragments.d.ts +0 -42
  70. package/example/node_modules/quantumcoin/src/abi/fragments.js +0 -63
  71. package/example/node_modules/quantumcoin/src/abi/index.d.ts +0 -13
  72. package/example/node_modules/quantumcoin/src/abi/index.js +0 -9
  73. package/example/node_modules/quantumcoin/src/abi/interface.d.ts +0 -128
  74. package/example/node_modules/quantumcoin/src/abi/interface.js +0 -605
  75. package/example/node_modules/quantumcoin/src/abi/js-abi-coder.d.ts +0 -8
  76. package/example/node_modules/quantumcoin/src/abi/js-abi-coder.js +0 -538
  77. package/example/node_modules/quantumcoin/src/constants.d.ts +0 -61
  78. package/example/node_modules/quantumcoin/src/constants.js +0 -94
  79. package/example/node_modules/quantumcoin/src/contract/contract-factory.d.ts +0 -28
  80. package/example/node_modules/quantumcoin/src/contract/contract-factory.js +0 -111
  81. package/example/node_modules/quantumcoin/src/contract/contract.d.ts +0 -113
  82. package/example/node_modules/quantumcoin/src/contract/contract.js +0 -408
  83. package/example/node_modules/quantumcoin/src/contract/index.d.ts +0 -9
  84. package/example/node_modules/quantumcoin/src/contract/index.js +0 -9
  85. package/example/node_modules/quantumcoin/src/errors/index.d.ts +0 -92
  86. package/example/node_modules/quantumcoin/src/errors/index.js +0 -200
  87. package/example/node_modules/quantumcoin/src/generator/index.d.ts +0 -81
  88. package/example/node_modules/quantumcoin/src/generator/index.js +0 -1571
  89. package/example/node_modules/quantumcoin/src/index.d.ts +0 -122
  90. package/example/node_modules/quantumcoin/src/index.js +0 -41
  91. package/example/node_modules/quantumcoin/src/internal/hex.d.ts +0 -68
  92. package/example/node_modules/quantumcoin/src/internal/hex.js +0 -180
  93. package/example/node_modules/quantumcoin/src/providers/extra-providers.d.ts +0 -139
  94. package/example/node_modules/quantumcoin/src/providers/extra-providers.js +0 -614
  95. package/example/node_modules/quantumcoin/src/providers/index.d.ts +0 -17
  96. package/example/node_modules/quantumcoin/src/providers/index.js +0 -10
  97. package/example/node_modules/quantumcoin/src/providers/json-rpc-provider.d.ts +0 -12
  98. package/example/node_modules/quantumcoin/src/providers/json-rpc-provider.js +0 -89
  99. package/example/node_modules/quantumcoin/src/providers/provider.d.ts +0 -206
  100. package/example/node_modules/quantumcoin/src/providers/provider.js +0 -524
  101. package/example/node_modules/quantumcoin/src/types/index.d.ts +0 -214
  102. package/example/node_modules/quantumcoin/src/types/index.js +0 -9
  103. package/example/node_modules/quantumcoin/src/utils/address.d.ts +0 -58
  104. package/example/node_modules/quantumcoin/src/utils/address.js +0 -144
  105. package/example/node_modules/quantumcoin/src/utils/encoding.d.ts +0 -120
  106. package/example/node_modules/quantumcoin/src/utils/encoding.js +0 -306
  107. package/example/node_modules/quantumcoin/src/utils/fixednumber.d.ts +0 -57
  108. package/example/node_modules/quantumcoin/src/utils/fixednumber.js +0 -366
  109. package/example/node_modules/quantumcoin/src/utils/hashing.d.ts +0 -76
  110. package/example/node_modules/quantumcoin/src/utils/hashing.js +0 -298
  111. package/example/node_modules/quantumcoin/src/utils/index.d.ts +0 -63
  112. package/example/node_modules/quantumcoin/src/utils/index.js +0 -14
  113. package/example/node_modules/quantumcoin/src/utils/result.d.ts +0 -57
  114. package/example/node_modules/quantumcoin/src/utils/result.js +0 -128
  115. package/example/node_modules/quantumcoin/src/utils/rlp.d.ts +0 -12
  116. package/example/node_modules/quantumcoin/src/utils/rlp.js +0 -215
  117. package/example/node_modules/quantumcoin/src/utils/units.d.ts +0 -29
  118. package/example/node_modules/quantumcoin/src/utils/units.js +0 -107
  119. package/example/node_modules/quantumcoin/src/wallet/index.d.ts +0 -10
  120. package/example/node_modules/quantumcoin/src/wallet/index.js +0 -8
  121. package/example/node_modules/quantumcoin/src/wallet/wallet.d.ts +0 -192
  122. package/example/node_modules/quantumcoin/src/wallet/wallet.js +0 -713
  123. package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.dynamic.test.js +0 -207
  124. package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.dynamic.test.ts +0 -191
  125. package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.fixtures.js +0 -231
  126. package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.generated-sdks.e2e.test.js +0 -387
  127. package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.generated-sdks.e2e.test.ts +0 -350
  128. package/example/node_modules/quantumcoin/test/e2e/generator-interface.e2e.test.js +0 -165
  129. package/example/node_modules/quantumcoin/test/e2e/generator-interface.e2e.test.ts +0 -160
  130. package/example/node_modules/quantumcoin/test/e2e/helpers.js +0 -59
  131. package/example/node_modules/quantumcoin/test/e2e/signing-context-and-fee.e2e.test.js +0 -141
  132. package/example/node_modules/quantumcoin/test/e2e/signing-context-and-fee.e2e.test.ts +0 -128
  133. package/example/node_modules/quantumcoin/test/e2e/simple-erc20.generated-sdks.e2e.test.js +0 -168
  134. package/example/node_modules/quantumcoin/test/e2e/simple-erc20.generated-sdks.e2e.test.ts +0 -141
  135. package/example/node_modules/quantumcoin/test/e2e/transactional.test.js +0 -245
  136. package/example/node_modules/quantumcoin/test/e2e/transactional.test.ts +0 -208
  137. package/example/node_modules/quantumcoin/test/e2e/typed-generator.e2e.test.js +0 -407
  138. package/example/node_modules/quantumcoin/test/e2e/typed-generator.e2e.test.ts +0 -337
  139. package/example/node_modules/quantumcoin/test/fixtures/ConstructorParam.sol +0 -23
  140. package/example/node_modules/quantumcoin/test/fixtures/MultiContracts.sol +0 -37
  141. package/example/node_modules/quantumcoin/test/fixtures/SimpleStorage.sol +0 -18
  142. package/example/node_modules/quantumcoin/test/fixtures/StakingContract.abi.json +0 -1
  143. package/example/node_modules/quantumcoin/test/integration/ipc-provider.test.js +0 -49
  144. package/example/node_modules/quantumcoin/test/integration/ipc-provider.test.ts +0 -44
  145. package/example/node_modules/quantumcoin/test/integration/provider.test.js +0 -88
  146. package/example/node_modules/quantumcoin/test/integration/provider.test.ts +0 -85
  147. package/example/node_modules/quantumcoin/test/integration/ws-provider.test.js +0 -41
  148. package/example/node_modules/quantumcoin/test/integration/ws-provider.test.ts +0 -38
  149. package/example/node_modules/quantumcoin/test/security/abi-decoder-bounds.test.js +0 -122
  150. package/example/node_modules/quantumcoin/test/security/contract-overrides.test.js +0 -112
  151. package/example/node_modules/quantumcoin/test/security/generator-injection.test.js +0 -195
  152. package/example/node_modules/quantumcoin/test/security/malformed-input.test.js +0 -330
  153. package/example/node_modules/quantumcoin/test/security/malformed-input.test.ts +0 -35
  154. package/example/node_modules/quantumcoin/test/security/rpc-numeric-bounds.test.js +0 -81
  155. package/example/node_modules/quantumcoin/test/security/rpc-trust.test.js +0 -202
  156. package/example/node_modules/quantumcoin/test/unit/_encrypted-output.txt +0 -6
  157. package/example/node_modules/quantumcoin/test/unit/_log-encrypted-jsons.js +0 -45
  158. package/example/node_modules/quantumcoin/test/unit/_write-keystore-fixture.js +0 -16
  159. package/example/node_modules/quantumcoin/test/unit/abi-interface.test.js +0 -110
  160. package/example/node_modules/quantumcoin/test/unit/abi-interface.test.ts +0 -109
  161. package/example/node_modules/quantumcoin/test/unit/address-wallet.test.js +0 -923
  162. package/example/node_modules/quantumcoin/test/unit/address-wallet.test.ts +0 -877
  163. package/example/node_modules/quantumcoin/test/unit/browser-provider.test.js +0 -85
  164. package/example/node_modules/quantumcoin/test/unit/browser-provider.test.ts +0 -79
  165. package/example/node_modules/quantumcoin/test/unit/contract.test.js +0 -85
  166. package/example/node_modules/quantumcoin/test/unit/contract.test.ts +0 -83
  167. package/example/node_modules/quantumcoin/test/unit/encoding-units-rlp.test.js +0 -127
  168. package/example/node_modules/quantumcoin/test/unit/encoding-units-rlp.test.ts +0 -91
  169. package/example/node_modules/quantumcoin/test/unit/errors.test.js +0 -77
  170. package/example/node_modules/quantumcoin/test/unit/errors.test.ts +0 -76
  171. package/example/node_modules/quantumcoin/test/unit/filter-by-blockhash.test.js +0 -55
  172. package/example/node_modules/quantumcoin/test/unit/filter-by-blockhash.test.ts +0 -54
  173. package/example/node_modules/quantumcoin/test/unit/fixednumber.test.js +0 -656
  174. package/example/node_modules/quantumcoin/test/unit/fixednumber.test.ts +0 -660
  175. package/example/node_modules/quantumcoin/test/unit/fixtures/encrypted-keystores-48-32-36.js +0 -9
  176. package/example/node_modules/quantumcoin/test/unit/generate-contract-cli.test.js +0 -42
  177. package/example/node_modules/quantumcoin/test/unit/generate-contract-cli.test.ts +0 -41
  178. package/example/node_modules/quantumcoin/test/unit/generate-sdk-artifacts-json.test.js +0 -113
  179. package/example/node_modules/quantumcoin/test/unit/generate-sdk-artifacts-json.test.ts +0 -110
  180. package/example/node_modules/quantumcoin/test/unit/generator.test.js +0 -149
  181. package/example/node_modules/quantumcoin/test/unit/generator.test.ts +0 -148
  182. package/example/node_modules/quantumcoin/test/unit/hashing.test.js +0 -64
  183. package/example/node_modules/quantumcoin/test/unit/hashing.test.ts +0 -63
  184. package/example/node_modules/quantumcoin/test/unit/init.test.js +0 -39
  185. package/example/node_modules/quantumcoin/test/unit/init.test.ts +0 -38
  186. package/example/node_modules/quantumcoin/test/unit/interface.test.js +0 -56
  187. package/example/node_modules/quantumcoin/test/unit/interface.test.ts +0 -54
  188. package/example/node_modules/quantumcoin/test/unit/internal-hex.test.js +0 -81
  189. package/example/node_modules/quantumcoin/test/unit/internal-hex.test.ts +0 -80
  190. package/example/node_modules/quantumcoin/test/unit/populate-transaction.test.js +0 -98
  191. package/example/node_modules/quantumcoin/test/unit/populate-transaction.test.ts +0 -64
  192. package/example/node_modules/quantumcoin/test/unit/providers.test.js +0 -252
  193. package/example/node_modules/quantumcoin/test/unit/providers.test.ts +0 -251
  194. package/example/node_modules/quantumcoin/test/unit/result.test.js +0 -80
  195. package/example/node_modules/quantumcoin/test/unit/result.test.ts +0 -79
  196. package/example/node_modules/quantumcoin/test/unit/solidity-types.test.js +0 -49
  197. package/example/node_modules/quantumcoin/test/unit/solidity-types.test.ts +0 -39
  198. package/example/node_modules/quantumcoin/test/unit/utils.test.js +0 -57
  199. package/example/node_modules/quantumcoin/test/unit/utils.test.ts +0 -56
  200. package/example/node_modules/quantumcoin/test/verbose-logger.js +0 -74
  201. package/example/node_modules/quantumcoin/tsconfig.build.json +0 -14
  202. package/example/node_modules/seed-words/.github/workflows/publish-npmjs.yaml +0 -22
  203. package/example/node_modules/seed-words/BUILD.md +0 -7
  204. package/example/node_modules/seed-words/LICENSE +0 -121
  205. package/example/node_modules/seed-words/README.md +0 -67
  206. package/example/node_modules/seed-words/dist/seedwords.d.ts +0 -39
  207. package/example/node_modules/seed-words/package.json +0 -27
  208. package/example/node_modules/seed-words/seedwords.js +0 -315
  209. package/example/node_modules/seed-words/seedwords.txt +0 -65536
  210. package/example/node_modules/seed-words/tsconfig.json +0 -21
  211. package/example/package-lock.json +0 -56
  212. package/example/package.json +0 -15
  213. package/tests/encrypted-32.json +0 -1
  214. package/tests/encrypted-36.json +0 -1
  215. package/tests/encrypted-48.json +0 -1
  216. package/tests/generate-verify-vectors.js +0 -91
  217. package/tests/get-gas-price.test.js +0 -59
  218. package/tests/non-transactional.preinit.test.js +0 -41
  219. package/tests/non-transactional.test.js +0 -1389
  220. package/tests/scrypt.test.js +0 -128
  221. package/tests/sign-raw-keytype5-context-null.test.js +0 -107
  222. package/tests/sign-raw-transaction.test.js +0 -196
  223. package/tests/sign-verify.test.js +0 -311
  224. package/tests/transactional.relay.test.js +0 -131
  225. package/tests/transactional.rpc.test.js +0 -103
  226. package/tests/verify-vectors.json +0 -95035
@@ -1,1571 +0,0 @@
1
- /**
2
- * @fileoverview Typed contract generator (SPEC.md section 15).
3
- *
4
- * Supports generating:
5
- * - TypeScript source (`.ts`)
6
- * - JavaScript source (`.js`) + TypeScript declaration files (`.d.ts`)
7
- *
8
- * It is designed to be invoked by `generate-sdk.js` (CLI) and
9
- * can also be imported as a library.
10
- */
11
-
12
- const fs = require("node:fs");
13
- const path = require("node:path");
14
-
15
- function _ensureDir(p) {
16
- fs.mkdirSync(p, { recursive: true });
17
- }
18
-
19
- function _readJson(filePath) {
20
- return JSON.parse(fs.readFileSync(filePath, "utf8"));
21
- }
22
-
23
- function _stripArraySuffixes(s) {
24
- let out = String(s || "");
25
- while (out.endsWith("]")) {
26
- out = out.slice(0, out.lastIndexOf("["));
27
- }
28
- return out;
29
- }
30
-
31
- function _parseArray(type) {
32
- const t = String(type || "");
33
- const idx = t.lastIndexOf("[");
34
- if (idx < 0 || !t.endsWith("]")) return null;
35
- const inner = t.slice(0, idx);
36
- const bracket = t.slice(idx + 1, t.length - 1); // "" => dynamic
37
- const fixedLen = bracket.length ? Number(bracket) : null;
38
- return { inner, fixedLen: fixedLen != null && Number.isFinite(fixedLen) ? fixedLen : null };
39
- }
40
-
41
- function _tupleBaseNameFromInternalType(contractName, internalType) {
42
- const raw = String(internalType || "");
43
- if (!raw) return null;
44
- const cleaned = _stripArraySuffixes(raw);
45
- const m = cleaned.match(/struct\s+([A-Za-z0-9_]+)\.([A-Za-z0-9_]+)/);
46
- if (m && m[2]) return m[2];
47
- // Some compilers omit "struct" keyword.
48
- const m2 = cleaned.match(/^([A-Za-z0-9_]+)\.([A-Za-z0-9_]+)$/);
49
- if (m2 && m2[2]) return m2[2];
50
- // Fallback: last segment
51
- const parts = cleaned.split(".");
52
- const last = parts[parts.length - 1];
53
- if (last && last !== contractName) return last;
54
- return null;
55
- }
56
-
57
- function _tupleKey(param) {
58
- /** @param {any} p */
59
- function norm(p) {
60
- const out = {
61
- name: String(p && p.name ? p.name : ""),
62
- type: String(p && p.type ? p.type : ""),
63
- internalType: String(p && p.internalType ? p.internalType : ""),
64
- components: [],
65
- };
66
- const comps = Array.isArray(p && p.components) ? p.components : [];
67
- out.components = comps.map((c) => norm(c));
68
- return out;
69
- }
70
- return JSON.stringify(norm(param || {}));
71
- }
72
-
73
- function _collectTupleRegistry(contractName, abi) {
74
- const byKey = new Map(); // key -> baseName
75
- const usedNames = new Map(); // baseName -> key
76
- let counter = 0;
77
-
78
- /** @param {any} param */
79
- function ensureTuple(param) {
80
- const key = _tupleKey(param);
81
- if (byKey.has(key)) return byKey.get(key);
82
-
83
- const suggested = _safeTypeIdent(
84
- _tupleBaseNameFromInternalType(contractName, param && param.internalType) || `${contractName}Tuple${++counter}`,
85
- );
86
- let baseName = suggested;
87
- if (usedNames.has(baseName) && usedNames.get(baseName) !== key) {
88
- let n = 1;
89
- while (usedNames.has(`${suggested}_${n}`) && usedNames.get(`${suggested}_${n}`) !== key) n++;
90
- baseName = `${suggested}_${n}`;
91
- }
92
- byKey.set(key, baseName);
93
- usedNames.set(baseName, key);
94
-
95
- // Recurse to nested tuples.
96
- const comps = Array.isArray(param && param.components) ? param.components : [];
97
- for (const c of comps) visitParam(c);
98
- return baseName;
99
- }
100
-
101
- /** @param {any} param */
102
- function visitParam(param) {
103
- const type = String(param && param.type ? param.type : "");
104
- if (!type) return;
105
- const arr = _parseArray(type);
106
- if (arr) {
107
- visitParam({ ...(param || {}), type: arr.inner });
108
- return;
109
- }
110
- if (type === "tuple") {
111
- ensureTuple(param);
112
- return;
113
- }
114
- }
115
-
116
- for (const f of abi || []) {
117
- if (!f || typeof f !== "object") continue;
118
- const inputs = Array.isArray(f.inputs) ? f.inputs : [];
119
- const outputs = Array.isArray(f.outputs) ? f.outputs : [];
120
- for (const p of inputs) visitParam(p);
121
- for (const p of outputs) visitParam(p);
122
- }
123
-
124
- return { byKey };
125
- }
126
-
127
- function _solParamToTs(param, mode, tupleReg) {
128
- const type = String(param && param.type ? param.type : "");
129
- const m = mode === "output" ? "output" : "input";
130
-
131
- const arr = _parseArray(type);
132
- if (arr) {
133
- const innerParam = { ...(param || {}), type: arr.inner };
134
- const innerTs = _solParamToTs(innerParam, mode, tupleReg);
135
- if (arr.fixedLen != null) return `Types.SolFixedArray<${innerTs}, ${arr.fixedLen}>`;
136
- return `Types.SolArray<${innerTs}>`;
137
- }
138
-
139
- if (type === "tuple") {
140
- const key = _tupleKey(param);
141
- const baseName = tupleReg && tupleReg.byKey ? tupleReg.byKey.get(key) : null;
142
- const n = baseName || "Tuple";
143
- return `${n}${m === "input" ? "Input" : "Output"}`;
144
- }
145
-
146
- // Elementary types (hard typed)
147
- if (type === "address") return m === "input" ? "Types.AddressLike" : "Types.SolAddress";
148
- if (type === "bool") return "boolean";
149
- if (type === "string") return "string";
150
- if (type === "bytes") return m === "input" ? "Types.BytesLike" : "Types.HexString";
151
-
152
- const mBytesN = type.match(/^bytes(\d+)$/);
153
- if (mBytesN) {
154
- const n = Number(mBytesN[1]);
155
- if (n === 32) return m === "input" ? "Types.Bytes32Like" : "Types.Bytes32";
156
- if (Number.isFinite(n) && n >= 1 && n <= 32) return m === "input" ? `Types.Bytes${n}Like` : `Types.Bytes${n}`;
157
- }
158
-
159
- const mUint = type === "uint" ? ["uint", "256"] : type.match(/^uint(\d+)$/);
160
- if (mUint) {
161
- const bits = type === "uint" ? 256 : Number(mUint[1]);
162
- const b = Number.isFinite(bits) ? bits : 256;
163
- return m === "input" ? `Types.Uint${b}Like` : `Types.Uint${b}`;
164
- }
165
-
166
- const mInt = type === "int" ? ["int", "256"] : type.match(/^int(\d+)$/);
167
- if (mInt) {
168
- const bits = type === "int" ? 256 : Number(mInt[1]);
169
- const b = Number.isFinite(bits) ? bits : 256;
170
- return m === "input" ? `Types.Int${b}Like` : `Types.Int${b}`;
171
- }
172
-
173
- // Fallback (unknown type)
174
- return "any";
175
- }
176
-
177
- function _solParamToJsDoc(param, mode, tupleReg) {
178
- const type = String(param && param.type ? param.type : "");
179
- const m = mode === "output" ? "output" : "input";
180
-
181
- const arr = _parseArray(type);
182
- if (arr) {
183
- const innerParam = { ...(param || {}), type: arr.inner };
184
- const inner = _solParamToJsDoc(innerParam, mode, tupleReg);
185
- return `Array<${inner}>`;
186
- }
187
-
188
- if (type === "tuple") {
189
- const key = _tupleKey(param);
190
- const baseName = tupleReg && tupleReg.byKey ? tupleReg.byKey.get(key) : null;
191
- const n = baseName || "Tuple";
192
- return `${n}${m === "input" ? "Input" : "Output"}`;
193
- }
194
-
195
- if (type === "address") {
196
- return m === "input" ? `import("quantumcoin/types").AddressLike` : `import("quantumcoin/types").SolAddress`;
197
- }
198
- if (type === "bool") return "boolean";
199
- if (type === "string") return "string";
200
- if (type === "bytes") {
201
- return m === "input" ? `import("quantumcoin/types").BytesLike` : `import("quantumcoin/types").HexString`;
202
- }
203
-
204
- const mBytesN = type.match(/^bytes(\d+)$/);
205
- if (mBytesN) {
206
- const n = Number(mBytesN[1]);
207
- if (n === 32) {
208
- return m === "input" ? `import("quantumcoin/types").Bytes32Like` : `import("quantumcoin/types").Bytes32`;
209
- }
210
- if (Number.isFinite(n) && n >= 1 && n <= 32) {
211
- return m === "input"
212
- ? `import("quantumcoin/types").Bytes${n}Like`
213
- : `import("quantumcoin/types").Bytes${n}`;
214
- }
215
- }
216
-
217
- const mUint = type === "uint" ? ["uint", "256"] : type.match(/^uint(\d+)$/);
218
- if (mUint) {
219
- const bits = type === "uint" ? 256 : Number(mUint[1]);
220
- const b = Number.isFinite(bits) ? bits : 256;
221
- return m === "input"
222
- ? `import("quantumcoin/types").Uint${b}Like`
223
- : `import("quantumcoin/types").Uint${b}`;
224
- }
225
-
226
- const mInt = type === "int" ? ["int", "256"] : type.match(/^int(\d+)$/);
227
- if (mInt) {
228
- const bits = type === "int" ? 256 : Number(mInt[1]);
229
- const b = Number.isFinite(bits) ? bits : 256;
230
- return m === "input"
231
- ? `import("quantumcoin/types").Int${b}Like`
232
- : `import("quantumcoin/types").Int${b}`;
233
- }
234
-
235
- return "any";
236
- }
237
-
238
- function _renderTupleTypeDefs(contractName, abi, tupleReg) {
239
- const lines = [];
240
- void contractName;
241
-
242
- function _findTupleParamByKey(key) {
243
- /** @param {any} p */
244
- function walkParam(p) {
245
- if (!p || typeof p !== "object") return null;
246
- const t = String(p.type || "");
247
- if (!t) return null;
248
- const arr = _parseArray(t);
249
- if (arr) return walkParam({ ...(p || {}), type: arr.inner });
250
- if (t === "tuple") {
251
- if (_tupleKey(p) === key) return p;
252
- const comps = Array.isArray(p.components) ? p.components : [];
253
- for (const c of comps) {
254
- const found = walkParam(c);
255
- if (found) return found;
256
- }
257
- }
258
- return null;
259
- }
260
-
261
- for (const f of abi || []) {
262
- if (!f || typeof f !== "object") continue;
263
- const inputs = Array.isArray(f.inputs) ? f.inputs : [];
264
- const outputs = Array.isArray(f.outputs) ? f.outputs : [];
265
- for (const p of inputs) {
266
- const found = walkParam(p);
267
- if (found) return found;
268
- }
269
- for (const p of outputs) {
270
- const found = walkParam(p);
271
- if (found) return found;
272
- }
273
- }
274
- return null;
275
- }
276
-
277
- /** @param {any} param */
278
- function tupleFields(param, mode) {
279
- const comps = Array.isArray(param && param.components) ? param.components : [];
280
- const fields = [];
281
- for (let i = 0; i < comps.length; i++) {
282
- const c = comps[i];
283
- const field = _safeIdent((c && c.name) || `field${i}`);
284
- fields.push(` ${field}: ${_solParamToTs(c, mode, tupleReg)};`);
285
- }
286
- return fields;
287
- }
288
-
289
- // Render each known tuple once (as Input + Output).
290
- for (const [key, baseName] of tupleReg.byKey.entries()) {
291
- const param = _findTupleParamByKey(key);
292
- if (!param) continue;
293
-
294
- lines.push(`export type ${baseName}Input = {`);
295
- lines.push(...tupleFields(param, "input"));
296
- lines.push(`};`);
297
- lines.push(``);
298
- lines.push(`export type ${baseName}Output = {`);
299
- lines.push(...tupleFields(param, "output"));
300
- lines.push(`};`);
301
- lines.push(``);
302
- }
303
-
304
- return lines.length ? lines.join("\n") + "\n" : "";
305
- }
306
-
307
- function _renderTupleJsDocTypeDefs(contractName, abi, tupleReg) {
308
- void contractName;
309
- if (!tupleReg || !tupleReg.byKey || tupleReg.byKey.size === 0) return "";
310
-
311
- const lines = [];
312
-
313
- /** @param {string} key */
314
- function findTupleParamByKey(key) {
315
- /** @param {any} p */
316
- function walkParam(p) {
317
- if (!p || typeof p !== "object") return null;
318
- const t = String(p.type || "");
319
- if (!t) return null;
320
- const arr = _parseArray(t);
321
- if (arr) return walkParam({ ...(p || {}), type: arr.inner });
322
- if (t === "tuple") {
323
- if (_tupleKey(p) === key) return p;
324
- const comps = Array.isArray(p.components) ? p.components : [];
325
- for (const c of comps) {
326
- const found = walkParam(c);
327
- if (found) return found;
328
- }
329
- }
330
- return null;
331
- }
332
-
333
- for (const f of abi || []) {
334
- if (!f || typeof f !== "object") continue;
335
- const inputs = Array.isArray(f.inputs) ? f.inputs : [];
336
- const outputs = Array.isArray(f.outputs) ? f.outputs : [];
337
- for (const p of inputs) {
338
- const found = walkParam(p);
339
- if (found) return found;
340
- }
341
- for (const p of outputs) {
342
- const found = walkParam(p);
343
- if (found) return found;
344
- }
345
- }
346
- return null;
347
- }
348
-
349
- function renderTypedef(typeName, tupleParam, mode) {
350
- const comps = Array.isArray(tupleParam && tupleParam.components) ? tupleParam.components : [];
351
- lines.push("/**");
352
- lines.push(` * @typedef {Object} ${typeName}`);
353
- for (let i = 0; i < comps.length; i++) {
354
- const c = comps[i];
355
- const field = _safeIdent((c && c.name) || `field${i}`);
356
- lines.push(` * @property {${_solParamToJsDoc(c, mode, tupleReg)}} ${field}`);
357
- }
358
- lines.push(" */");
359
- lines.push("");
360
- }
361
-
362
- for (const [key, baseName] of tupleReg.byKey.entries()) {
363
- const param = findTupleParamByKey(key);
364
- if (!param) continue;
365
- renderTypedef(`${baseName}Input`, param, "input");
366
- renderTypedef(`${baseName}Output`, param, "output");
367
- }
368
-
369
- return lines.join("\n");
370
- }
371
-
372
- function _cap(s) {
373
- return s ? s[0].toUpperCase() + s.slice(1) : s;
374
- }
375
-
376
- function _safeIdent(name) {
377
- return (name || "arg").replace(/[^a-zA-Z0-9_]/g, "_");
378
- }
379
-
380
- // NatSpec/doc text is attacker-controllable (it comes from arbitrary Solidity
381
- // source). It is emitted into generated `/** ... */` JSDoc blocks, so a `*/` in the
382
- // text would close the comment early and turn whatever follows into executable code
383
- // in the generated .js/.ts files. Neutralize the comment delimiters (and strip
384
- // CR/LF so a line cannot be split) before interpolation.
385
- function _escapeComment(text) {
386
- return String(text == null ? "" : text)
387
- .replace(/\*\//g, "* /")
388
- .replace(/\/\*/g, "/ *")
389
- .replace(/[\r\n]+/g, " ");
390
- }
391
-
392
- // Reserved words / dangerous property names that must never be emitted as a raw
393
- // identifier (class name, function name, import/require specifier, etc.).
394
- const _RESERVED_IDENTIFIERS = new Set([
395
- "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do",
396
- "else", "export", "extends", "finally", "for", "function", "if", "import", "in", "instanceof",
397
- "new", "return", "super", "switch", "this", "throw", "try", "typeof", "var", "void", "while",
398
- "with", "yield", "enum", "await", "implements", "interface", "let", "package", "private",
399
- "protected", "public", "static", "null", "true", "false",
400
- "__proto__", "constructor", "prototype",
401
- ]);
402
-
403
- /**
404
- * Assert that `name` is safe to interpolate verbatim into generated source code
405
- * (class names, function names, type names, require/import specifiers, file names).
406
- *
407
- * This is the primary defense against code-injection and path-traversal
408
- * via attacker-controlled ABI / artifact `name` fields: only strict JS/TS
409
- * identifiers are allowed, which by construction cannot contain quotes, newlines,
410
- * path separators, `..`, or other breakout characters.
411
- *
412
- * @param {any} name
413
- * @param {string=} kind Human-readable description for error messages.
414
- * @returns {string} the validated name
415
- */
416
- function _assertSafeIdentifier(name, kind) {
417
- const label = kind || "identifier";
418
- if (typeof name !== "string" || !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) {
419
- throw new Error(`Unsafe ${label} for code generation: ${JSON.stringify(name)}`);
420
- }
421
- if (_RESERVED_IDENTIFIERS.has(name)) {
422
- throw new Error(`${label} must not be a reserved word: ${JSON.stringify(name)}`);
423
- }
424
- return name;
425
- }
426
-
427
- /**
428
- * Coerce an arbitrary string (e.g. a struct name derived from an attacker-controlled
429
- * `internalType`) into a safe type identifier. Unlike `_assertSafeIdentifier` this
430
- * never throws; it sanitizes so auto-generated type names always remain valid.
431
- * @param {any} s
432
- * @returns {string}
433
- */
434
- function _safeTypeIdent(s) {
435
- let v = String(s == null ? "" : s).replace(/[^A-Za-z0-9_$]/g, "_");
436
- if (!/^[A-Za-z_$]/.test(v)) v = "_" + v;
437
- return v || "_Tuple";
438
- }
439
-
440
- /**
441
- * Validate every rendered function name in an ABI. Only `function` entries are
442
- * emitted as method identifiers, so those are the names that must be safe.
443
- * @param {any[]} abi
444
- */
445
- function _assertSafeAbiNames(abi) {
446
- for (const f of abi || []) {
447
- if (f && f.type === "function" && f.name != null) {
448
- _assertSafeIdentifier(f.name, "ABI function name");
449
- }
450
- }
451
- }
452
-
453
- /**
454
- * Assert that `filePath` resolves to a location inside `outDir` (defense-in-depth).
455
- * @param {string} outDir
456
- * @param {string} filePath
457
- */
458
- function _assertWithinDir(outDir, filePath) {
459
- const root = path.resolve(outDir);
460
- const resolved = path.resolve(filePath);
461
- const rel = path.relative(root, resolved);
462
- if (rel.startsWith("..") || path.isAbsolute(rel)) {
463
- throw new Error(`Refusing to write outside output directory: ${resolved}`);
464
- }
465
- }
466
-
467
- function _findConstructor(abi) {
468
- const ctor = abi.find((f) => f && f.type === "constructor");
469
- return ctor || { type: "constructor", inputs: [] };
470
- }
471
-
472
- /**
473
- * Return true if the given bytecode artifact represents an interface
474
- * (or otherwise non-deployable contract with no runtime code).
475
- *
476
- * `solc` writes an empty `.bin` for `interface` declarations and fully
477
- * abstract `contract` declarations. We treat `null`, `undefined`, empty
478
- * string, and the bare `"0x"` / `"0X"` prefix as interface bytecode.
479
- *
480
- * @param {string|null|undefined} bytecode
481
- * @returns {boolean}
482
- */
483
- function _isInterfaceBytecode(bytecode) {
484
- if (bytecode == null) return true;
485
- const s = String(bytecode).trim().toLowerCase();
486
- return s === "" || s === "0x";
487
- }
488
-
489
- function _solTypeToTestValueExpr(param) {
490
- const type = typeof param === "string" ? param : String(param && param.type ? param.type : "");
491
- const internalType = typeof param === "object" && param ? String(param.internalType || "") : "";
492
-
493
- // Arrays (dynamic or fixed)
494
- if (type.endsWith("]")) {
495
- const inner = type.slice(0, type.lastIndexOf("["));
496
- const bracket = type.slice(type.lastIndexOf("[") + 1, type.length - 1);
497
- const isFixed = bracket.length > 0;
498
- const fixedLen = isFixed ? Number(bracket) : 0;
499
- const elemParam = { ...(param || {}), type: inner };
500
-
501
- const elemExpr = _solTypeToTestValueExpr(elemParam);
502
- if (isFixed && Number.isFinite(fixedLen) && fixedLen > 0) {
503
- // Fixed arrays MUST match the exact declared length.
504
- // Use Array(len).fill(expr) to keep source size reasonable.
505
- return `Array(${fixedLen}).fill(${elemExpr})`;
506
- }
507
- return `[${elemExpr}]`;
508
- }
509
-
510
- if (type === "address") return "wallet.address";
511
- if (type === "bool") return "true";
512
- if (type === "string") return JSON.stringify("hello");
513
- if (type === "bytes") return JSON.stringify("0x1234");
514
-
515
- // Fixed-size bytes
516
- const mBytesN = type.match(/^bytes(\d+)$/);
517
- if (mBytesN) {
518
- const n = Number(mBytesN[1]);
519
- if (Number.isFinite(n) && n >= 1 && n <= 32) return JSON.stringify(`0x${"11".repeat(n)}`);
520
- }
521
-
522
- // NOTE: quantum-coin-js-sdk WASM interop does not accept BigInt values directly.
523
- // Use plain numbers/strings for ints/uints.
524
- // Enums are ABI-encoded as uints but Solidity will revert if the value is out of range.
525
- if (type.startsWith("uint") && /\benum\b/.test(internalType)) return "1";
526
- if (type.startsWith("uint")) return "123";
527
- if (type.startsWith("int")) return "-123";
528
-
529
- // Tuple / struct
530
- if (type === "tuple") {
531
- const comps = Array.isArray(param && param.components) ? param.components : [];
532
- if (comps.length === 0) return "{}";
533
- const fields = comps.map((c, idx) => {
534
- const name = c && typeof c.name === "string" && c.name ? c.name : `field${idx}`;
535
- return `${JSON.stringify(name)}: ${_solTypeToTestValueExpr(c)}`;
536
- });
537
- return `{ ${fields.join(", ")} }`;
538
- }
539
-
540
- // Fallback
541
- return "undefined";
542
- }
543
-
544
- function _isSupportedType(type) {
545
- if (type.endsWith("]")) return _isSupportedType(type.slice(0, type.lastIndexOf("[")));
546
- return (
547
- type === "address" ||
548
- type === "bool" ||
549
- type === "string" ||
550
- type === "bytes" ||
551
- type === "bytes32" ||
552
- type.startsWith("uint") ||
553
- type.startsWith("int")
554
- );
555
- }
556
-
557
- function _allSupportedParams(inputs) {
558
- return (inputs || []).every((i) => i && typeof i.type === "string" && _isSupportedType(i.type));
559
- }
560
-
561
- function _typesTs() {
562
- return (
563
- `// Auto-generated by sdkgen\n\n` +
564
- `// Re-export ALL Solidity-related types from quantumcoin.\n` +
565
- `export type * from "quantumcoin/types";\n`
566
- );
567
- }
568
-
569
- function _typesJs() {
570
- return `// Auto-generated by sdkgen\n\n` + `module.exports = {};\n`;
571
- }
572
-
573
- function _typesDts() {
574
- return (
575
- `// Auto-generated by sdkgen\n\n` +
576
- `export type * from "quantumcoin/types";\n`
577
- );
578
- }
579
-
580
- function _renderContractTs({ contractName, abi, bytecode, docs }) {
581
- const functions = (abi || []).filter((f) => f && f.type === "function");
582
- const tupleReg = _collectTupleRegistry(contractName, abi);
583
- const txFns = functions.filter((f) => (f.stateMutability || "") !== "view" && (f.stateMutability || "") !== "pure");
584
-
585
- const contractTsLines = [];
586
- contractTsLines.push(`// Auto-generated by sdkgen`);
587
- contractTsLines.push(`import { Contract, ContractTransactionResponse, ContractRunner, TransactionResponse } from "quantumcoin";`);
588
- contractTsLines.push(`import type * as Types from "./types";`);
589
- contractTsLines.push(``);
590
- contractTsLines.push(_renderTupleTypeDefs(contractName, abi, tupleReg).trimEnd());
591
- contractTsLines.push(``);
592
- contractTsLines.push(`/**`);
593
- contractTsLines.push(` * ${contractName} - A typed contract interface for ${contractName}`);
594
- if (docs && typeof docs.contract === "string" && docs.contract.trim()) {
595
- contractTsLines.push(` *`);
596
- for (const line of docs.contract.split(/\r?\n/g)) {
597
- const t = line.trim();
598
- if (!t) continue;
599
- contractTsLines.push(` * ${_escapeComment(t)}`);
600
- }
601
- }
602
- contractTsLines.push(` */`);
603
- contractTsLines.push(`export class ${contractName} extends Contract {`);
604
- contractTsLines.push(` static readonly abi = ${JSON.stringify(abi, null, 2)} as const;`);
605
- contractTsLines.push(` static readonly bytecode = ${JSON.stringify(bytecode)};`);
606
- contractTsLines.push(``);
607
- contractTsLines.push(` static connect(address: string, runner?: ContractRunner): ${contractName} {`);
608
- contractTsLines.push(` return new ${contractName}(address, runner);`);
609
- contractTsLines.push(` }`);
610
- contractTsLines.push(``);
611
- contractTsLines.push(` constructor(address: string, runner?: ContractRunner, _deployTx?: TransactionResponse) {`);
612
- contractTsLines.push(` super(address, ${contractName}.abi as any, runner as any, ${contractName}.bytecode);`);
613
- contractTsLines.push(` // @ts-expect-error internal attach`);
614
- contractTsLines.push(` this._deployTx = _deployTx;`);
615
- if (txFns.length) {
616
- contractTsLines.push(``);
617
- contractTsLines.push(` // Typed populateTransaction helpers (offline signing / sendRawTransaction flows)`);
618
- contractTsLines.push(` this.populateTransaction = {`);
619
- for (const fn of txFns) {
620
- const name = fn.name;
621
- const inputs = fn.inputs || [];
622
- const argsSig = inputs
623
- .map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`)
624
- .join(", ");
625
- const argsNames = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
626
- contractTsLines.push(
627
- ` ${name}: async (${argsSig}${argsSig ? ", " : ""}overrides?: any): Promise<import("quantumcoin").TransactionRequest> => {`,
628
- );
629
- contractTsLines.push(` const data = this.interface.encodeFunctionData(${JSON.stringify(name)}, [${argsNames}]);`);
630
- // Drop attacker-controllable to/data/from; protected fields win.
631
- contractTsLines.push(` const safeOverrides: any = {};`);
632
- contractTsLines.push(
633
- ` for (const k of ["value", "gasLimit", "gasPrice", "maxFeePerGas", "maxPriorityFeePerGas", "nonce", "chainId", "remarks", "signingContext"]) { if (overrides && overrides[k] !== undefined) safeOverrides[k] = overrides[k]; }`,
634
- );
635
- contractTsLines.push(` return { ...safeOverrides, to: this.address, data };`);
636
- contractTsLines.push(` },`);
637
- }
638
- contractTsLines.push(` } as any;`);
639
- }
640
- contractTsLines.push(` }`);
641
-
642
- for (const fn of functions) {
643
- const name = fn.name;
644
- const inputs = fn.inputs || [];
645
- const outputs = fn.outputs || [];
646
- const argsSig = inputs
647
- .map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`)
648
- .join(", ");
649
- const argsNames = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
650
-
651
- const mut = fn.stateMutability || "";
652
- const isView = mut === "view" || mut === "pure";
653
-
654
- let returnTs;
655
- if (isView) {
656
- if (outputs.length === 0) returnTs = "Promise<void>";
657
- else if (outputs.length === 1) returnTs = `Promise<${_solParamToTs(outputs[0], "output", tupleReg)}>`;
658
- else returnTs = `Promise<[${outputs.map((o) => _solParamToTs(o, "output", tupleReg)).join(", ")}]>`;
659
- } else {
660
- returnTs = "Promise<ContractTransactionResponse>";
661
- }
662
-
663
- contractTsLines.push(``);
664
- contractTsLines.push(` /**`);
665
- contractTsLines.push(` * ${name}`);
666
- const fnDoc = docs && docs.functions && typeof docs.functions[name] === "string" ? docs.functions[name] : "";
667
- if (fnDoc && fnDoc.trim()) {
668
- contractTsLines.push(` *`);
669
- for (const line of fnDoc.split(/\r?\n/g)) {
670
- const t = line.trim();
671
- if (!t) continue;
672
- contractTsLines.push(` * ${_escapeComment(t)}`);
673
- }
674
- }
675
- contractTsLines.push(` */`);
676
- if (isView) {
677
- contractTsLines.push(` async ${name}(${argsSig}): ${returnTs} {`);
678
- contractTsLines.push(` const res = await this.call(${JSON.stringify(name)}, [${argsNames}]);`);
679
- if (outputs.length === 0) {
680
- contractTsLines.push(` void res;`);
681
- contractTsLines.push(` return;`);
682
- } else if (outputs.length === 1) {
683
- contractTsLines.push(` return (Array.isArray(res) ? res[0] : res) as unknown as ${_solParamToTs(outputs[0], "output", tupleReg)};`);
684
- } else {
685
- contractTsLines.push(
686
- ` return res as unknown as [${outputs.map((o) => _solParamToTs(o, "output", tupleReg)).join(", ")}];`,
687
- );
688
- }
689
- contractTsLines.push(` }`);
690
- } else {
691
- contractTsLines.push(` async ${name}(${argsSig}${argsSig ? ", " : ""}overrides?: any): ${returnTs} {`);
692
- contractTsLines.push(` return this.send(${JSON.stringify(name)}, [${argsNames}], overrides);`);
693
- contractTsLines.push(` }`);
694
- }
695
- }
696
-
697
- contractTsLines.push(`}`);
698
- return contractTsLines.join("\n") + "\n";
699
- }
700
-
701
- function _renderContractJs({ contractName, abi, bytecode, docs }) {
702
- const functions = (abi || []).filter((f) => f && f.type === "function");
703
- const tupleReg = _collectTupleRegistry(contractName, abi);
704
- const txFns = functions.filter((f) => (f.stateMutability || "") !== "view" && (f.stateMutability || "") !== "pure");
705
-
706
- const lines = [];
707
- lines.push(`// Auto-generated by sdkgen`);
708
- lines.push(`const { Contract } = require("quantumcoin");`);
709
- lines.push("");
710
-
711
- const tupleDoc = _renderTupleJsDocTypeDefs(contractName, abi, tupleReg).trimEnd();
712
- if (tupleDoc) {
713
- lines.push(tupleDoc);
714
- lines.push("");
715
- }
716
-
717
- lines.push("/**");
718
- lines.push(` * ${contractName} - A typed contract interface for ${contractName}`);
719
- if (docs && typeof docs.contract === "string" && docs.contract.trim()) {
720
- lines.push(" *");
721
- for (const line of docs.contract.split(/\r?\n/g)) {
722
- const t = line.trim();
723
- if (!t) continue;
724
- lines.push(` * ${_escapeComment(t)}`);
725
- }
726
- }
727
- lines.push(" */");
728
- lines.push(`class ${contractName} extends Contract {`);
729
- lines.push(` static abi = ${JSON.stringify(abi, null, 2)};`);
730
- lines.push(` static bytecode = ${JSON.stringify(bytecode)};`);
731
- lines.push("");
732
- lines.push(` static connect(address, runner) {`);
733
- lines.push(` return new ${contractName}(address, runner);`);
734
- lines.push(` }`);
735
- lines.push("");
736
- lines.push(` constructor(address, runner, _deployTx) {`);
737
- lines.push(` super(address, ${contractName}.abi, runner, ${contractName}.bytecode);`);
738
- lines.push(` this._deployTx = _deployTx;`);
739
- if (txFns.length) {
740
- lines.push("");
741
- lines.push(" // Typed populateTransaction helpers (offline signing / sendRawTransaction flows)");
742
- lines.push(" this.populateTransaction = {");
743
- for (const fn of txFns) {
744
- const name = fn.name;
745
- const inputs = fn.inputs || [];
746
- const argsNames = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
747
- const argsSig = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
748
- lines.push(` ${name}: async (${argsSig}${argsSig ? ", " : ""}overrides) => {`);
749
- lines.push(` const data = this.interface.encodeFunctionData(${JSON.stringify(name)}, [${argsNames}]);`);
750
- // Drop attacker-controllable to/data/from; protected fields win.
751
- lines.push(` const safeOverrides = {};`);
752
- lines.push(
753
- ` for (const k of ["value", "gasLimit", "gasPrice", "maxFeePerGas", "maxPriorityFeePerGas", "nonce", "chainId", "remarks", "signingContext"]) { if (overrides && overrides[k] !== undefined) safeOverrides[k] = overrides[k]; }`,
754
- );
755
- lines.push(` return { ...safeOverrides, to: this.address, data };`);
756
- lines.push(" },");
757
- }
758
- lines.push(" };");
759
- }
760
- lines.push(` }`);
761
-
762
- for (const fn of functions) {
763
- const name = fn.name;
764
- const inputs = fn.inputs || [];
765
- const outputs = fn.outputs || [];
766
- const argsNames = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
767
-
768
- const mut = fn.stateMutability || "";
769
- const isView = mut === "view" || mut === "pure";
770
-
771
- lines.push("");
772
- lines.push(" /**");
773
- lines.push(` * ${name}`);
774
- const fnDoc = docs && docs.functions && typeof docs.functions[name] === "string" ? docs.functions[name] : "";
775
- if (fnDoc && fnDoc.trim()) {
776
- lines.push(" *");
777
- for (const line of fnDoc.split(/\r?\n/g)) {
778
- const t = line.trim();
779
- if (!t) continue;
780
- lines.push(` * ${_escapeComment(t)}`);
781
- }
782
- }
783
- for (const p of inputs) {
784
- const pName = _safeIdent(p.name || "arg");
785
- lines.push(` * @param {${_solParamToJsDoc(p, "input", tupleReg)}} ${pName}`);
786
- }
787
- if (isView) {
788
- if (outputs.length === 0) {
789
- lines.push(` * @returns {Promise<void>}`);
790
- } else if (outputs.length === 1) {
791
- lines.push(` * @returns {Promise<${_solParamToJsDoc(outputs[0], "output", tupleReg)}>} `);
792
- } else {
793
- lines.push(
794
- ` * @returns {Promise<[${outputs.map((o) => _solParamToJsDoc(o, "output", tupleReg)).join(", ")}]>}`,
795
- );
796
- }
797
- } else {
798
- lines.push(` * @returns {Promise<import("quantumcoin").ContractTransactionResponse>}`);
799
- }
800
- lines.push(" */");
801
-
802
- if (isView) {
803
- lines.push(` async ${name}(${argsNames}) {`);
804
- lines.push(` const res = await this.call(${JSON.stringify(name)}, [${argsNames}]);`);
805
- if (outputs.length === 0) {
806
- lines.push(` return;`);
807
- } else if (outputs.length === 1) {
808
- lines.push(` return Array.isArray(res) ? res[0] : res;`);
809
- } else {
810
- lines.push(` return res;`);
811
- }
812
- lines.push(` }`);
813
- } else {
814
- lines.push(` async ${name}(${argsNames}${argsNames ? ", " : ""}overrides) {`);
815
- lines.push(` return this.send(${JSON.stringify(name)}, [${argsNames}], overrides);`);
816
- lines.push(` }`);
817
- }
818
- }
819
-
820
- lines.push(`}`);
821
- lines.push("");
822
- lines.push(`module.exports = { ${contractName} };`);
823
- lines.push("");
824
- return lines.join("\n");
825
- }
826
-
827
- function _renderContractDts({ contractName, abi }) {
828
- const functions = (abi || []).filter((f) => f && f.type === "function");
829
- const tupleReg = _collectTupleRegistry(contractName, abi);
830
- const txFns = functions.filter((f) => (f.stateMutability || "") !== "view" && (f.stateMutability || "") !== "pure");
831
- const lines = [];
832
- lines.push(`// Auto-generated by sdkgen`);
833
- lines.push(`import { Contract, ContractRunner, ContractTransactionResponse, TransactionResponse } from "quantumcoin";`);
834
- lines.push(`import type * as Types from "./types";`);
835
- lines.push("");
836
- const tupleDefs = _renderTupleTypeDefs(contractName, abi, tupleReg).trimEnd();
837
- if (tupleDefs) {
838
- lines.push(tupleDefs);
839
- lines.push("");
840
- }
841
- lines.push(`export declare class ${contractName} extends Contract {`);
842
- lines.push(` static readonly abi: readonly any[];`);
843
- lines.push(` static readonly bytecode: string;`);
844
- lines.push(` static connect(address: string, runner?: ContractRunner): ${contractName};`);
845
- lines.push(` constructor(address: string, runner?: ContractRunner, _deployTx?: TransactionResponse);`);
846
- if (txFns.length) {
847
- lines.push(` readonly populateTransaction: {`);
848
- for (const fn of txFns) {
849
- const name = fn.name;
850
- const inputs = fn.inputs || [];
851
- const argsSig = inputs
852
- .map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`)
853
- .join(", ");
854
- lines.push(
855
- ` ${name}(${argsSig}${argsSig ? ", " : ""}overrides?: any): Promise<import("quantumcoin").TransactionRequest>;`,
856
- );
857
- }
858
- lines.push(` };`);
859
- }
860
-
861
- for (const fn of functions) {
862
- const name = fn.name;
863
- const inputs = fn.inputs || [];
864
- const outputs = fn.outputs || [];
865
-
866
- const argsSig = inputs.map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`).join(", ");
867
-
868
- const mut = fn.stateMutability || "";
869
- const isView = mut === "view" || mut === "pure";
870
-
871
- let returnTs;
872
- if (isView) {
873
- if (outputs.length === 0) returnTs = "Promise<void>";
874
- else if (outputs.length === 1) returnTs = `Promise<${_solParamToTs(outputs[0], "output", tupleReg)}>`;
875
- else returnTs = `Promise<[${outputs.map((o) => _solParamToTs(o, "output", tupleReg)).join(", ")}]>`;
876
- } else {
877
- returnTs = "Promise<ContractTransactionResponse>";
878
- }
879
-
880
- if (isView) {
881
- lines.push(` ${name}(${argsSig}): ${returnTs};`);
882
- } else {
883
- lines.push(` ${name}(${argsSig}${argsSig ? ", " : ""}overrides?: any): ${returnTs};`);
884
- }
885
- }
886
-
887
- lines.push(`}`);
888
- return lines.join("\n") + "\n";
889
- }
890
-
891
- function _renderFactoryTs({ contractName, abi }) {
892
- const factoryName = `${contractName}__factory`;
893
- const ctor = _findConstructor(abi);
894
- const ctorInputs = (ctor && ctor.inputs) || [];
895
- const tupleReg = _collectTupleRegistry(contractName, abi);
896
-
897
- const factoryTsLines = [];
898
- factoryTsLines.push(`// Auto-generated by sdkgen`);
899
- factoryTsLines.push(`import { ContractFactory, ContractRunner, getCreateAddress } from "quantumcoin";`);
900
- factoryTsLines.push(`import { ${contractName} } from "./${contractName}";`);
901
- factoryTsLines.push(`import type * as Types from "./types";`);
902
-
903
- // Import tuple input types used by the constructor (if any)
904
- const ctorTupleTypes = new Set();
905
- const visit = (p) => {
906
- const t = String(p && p.type ? p.type : "");
907
- const arr = _parseArray(t);
908
- if (arr) return visit({ ...(p || {}), type: arr.inner });
909
- if (t === "tuple") {
910
- const key = _tupleKey(p);
911
- const base = tupleReg.byKey.get(key);
912
- if (base) ctorTupleTypes.add(`${base}Input`);
913
- const comps = Array.isArray(p && p.components) ? p.components : [];
914
- for (const c of comps) visit(c);
915
- }
916
- };
917
- for (const p of ctorInputs) visit(p);
918
- if (ctorTupleTypes.size) {
919
- factoryTsLines.push(`import type { ${Array.from(ctorTupleTypes).sort().join(", ")} } from "./${contractName}";`);
920
- }
921
- factoryTsLines.push(``);
922
- factoryTsLines.push(`export class ${factoryName} extends ContractFactory {`);
923
- factoryTsLines.push(` constructor(runner: ContractRunner) {`);
924
- factoryTsLines.push(` super(${contractName}.abi as any, ${contractName}.bytecode as any, runner as any);`);
925
- factoryTsLines.push(` }`);
926
- factoryTsLines.push(``);
927
-
928
- // Typed deploy method (uses constructor args + optional overrides)
929
- const deployArgsSig = ctorInputs
930
- .map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`)
931
- .join(", ");
932
- const deployArgsNames = ctorInputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
933
-
934
- factoryTsLines.push(` async deploy(${deployArgsSig}${deployArgsSig ? ", " : ""}overrides?: any): Promise<${contractName}> {`);
935
- factoryTsLines.push(` const signer: any = (this as any).signer;`);
936
- factoryTsLines.push(` if (!signer) { throw new Error("missing signer"); }`);
937
- factoryTsLines.push(` const from: string = signer.getAddress ? await signer.getAddress() : signer.address;`);
938
- factoryTsLines.push(` const provider: any = signer.provider;`);
939
- factoryTsLines.push(` if (!provider || !provider.getTransactionCount) { throw new Error("missing provider"); }`);
940
- factoryTsLines.push(` let nonce: number;`);
941
- factoryTsLines.push(` try { nonce = await provider.getTransactionCount(from, "pending"); } catch { nonce = await provider.getTransactionCount(from, "latest"); }`);
942
- factoryTsLines.push(` const address = getCreateAddress({ from, nonce });`);
943
- factoryTsLines.push(` const txReq: any = this.getDeployTransaction(${deployArgsNames});`);
944
- // Only allow-listed override fields may reach the signer; protected fields
945
- // (to/data from txReq, and the computed nonce) always win.
946
- factoryTsLines.push(` const safeOverrides: any = {};`);
947
- factoryTsLines.push(
948
- ` for (const k of ["value", "gasLimit", "gasPrice", "maxFeePerGas", "maxPriorityFeePerGas", "remarks", "signingContext"]) { if (overrides && overrides[k] !== undefined) safeOverrides[k] = overrides[k]; }`,
949
- );
950
- factoryTsLines.push(` const tx = await signer.sendTransaction({ ...txReq, ...safeOverrides, nonce });`);
951
- factoryTsLines.push(` return new ${contractName}(address, signer as any, tx as any);`);
952
- factoryTsLines.push(` }`);
953
- factoryTsLines.push(``);
954
-
955
- factoryTsLines.push(` static connect(address: string, runner?: ContractRunner): ${contractName} {`);
956
- factoryTsLines.push(` return ${contractName}.connect(address, runner);`);
957
- factoryTsLines.push(` }`);
958
- factoryTsLines.push(`}`);
959
-
960
- return factoryTsLines.join("\n") + "\n";
961
- }
962
-
963
- function _renderFactoryJs({ contractName, abi }) {
964
- const factoryName = `${contractName}__factory`;
965
- const ctor = _findConstructor(abi);
966
- const ctorInputs = (ctor && ctor.inputs) || [];
967
- const deployArgsNames = ctorInputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
968
-
969
- const lines = [];
970
- lines.push(`// Auto-generated by sdkgen`);
971
- lines.push(`const { ContractFactory, getCreateAddress } = require("quantumcoin");`);
972
- lines.push(`const { ${contractName} } = require("./${contractName}");`);
973
- lines.push("");
974
- lines.push(`class ${factoryName} extends ContractFactory {`);
975
- lines.push(` constructor(runner) {`);
976
- lines.push(` super(${contractName}.abi, ${contractName}.bytecode, runner);`);
977
- lines.push(` }`);
978
- lines.push("");
979
- lines.push(` async deploy(${deployArgsNames}${deployArgsNames ? ", " : ""}overrides) {`);
980
- lines.push(` const signer = this.signer;`);
981
- lines.push(` if (!signer) { throw new Error("missing signer"); }`);
982
- lines.push(` const from = signer.getAddress ? await signer.getAddress() : signer.address;`);
983
- lines.push(` const provider = signer.provider;`);
984
- lines.push(` if (!provider || !provider.getTransactionCount) { throw new Error("missing provider"); }`);
985
- lines.push(` let nonce;`);
986
- lines.push(` try { nonce = await provider.getTransactionCount(from, "pending"); } catch { nonce = await provider.getTransactionCount(from, "latest"); }`);
987
- lines.push(` const address = getCreateAddress({ from, nonce });`);
988
- lines.push(` const txReq = this.getDeployTransaction(${deployArgsNames});`);
989
- // Only allow-listed override fields may reach the signer; protected fields
990
- // (to/data from txReq, and the computed nonce) always win.
991
- lines.push(` const safeOverrides = {};`);
992
- lines.push(
993
- ` for (const k of ["value", "gasLimit", "gasPrice", "maxFeePerGas", "maxPriorityFeePerGas", "remarks", "signingContext"]) { if (overrides && overrides[k] !== undefined) safeOverrides[k] = overrides[k]; }`,
994
- );
995
- lines.push(` const tx = await signer.sendTransaction({ ...txReq, ...safeOverrides, nonce });`);
996
- lines.push(` return new ${contractName}(address, signer, tx);`);
997
- lines.push(` }`);
998
- lines.push("");
999
- lines.push(` static connect(address, runner) {`);
1000
- lines.push(` return ${contractName}.connect(address, runner);`);
1001
- lines.push(` }`);
1002
- lines.push(`}`);
1003
- lines.push("");
1004
- lines.push(`module.exports = { ${factoryName} };`);
1005
- lines.push("");
1006
- return lines.join("\n");
1007
- }
1008
-
1009
- function _renderFactoryDts({ contractName, abi }) {
1010
- const factoryName = `${contractName}__factory`;
1011
- const ctor = _findConstructor(abi);
1012
- const ctorInputs = (ctor && ctor.inputs) || [];
1013
- const tupleReg = _collectTupleRegistry(contractName, abi);
1014
- const deployArgsSig = ctorInputs.map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`).join(", ");
1015
-
1016
- const lines = [];
1017
- lines.push(`// Auto-generated by sdkgen`);
1018
- lines.push(`import { ContractFactory, ContractRunner } from "quantumcoin";`);
1019
- lines.push(`import { ${contractName} } from "./${contractName}";`);
1020
- lines.push(`import type * as Types from "./types";`);
1021
-
1022
- const ctorTupleTypes = new Set();
1023
- const visit = (p) => {
1024
- const t = String(p && p.type ? p.type : "");
1025
- const arr = _parseArray(t);
1026
- if (arr) return visit({ ...(p || {}), type: arr.inner });
1027
- if (t === "tuple") {
1028
- const key = _tupleKey(p);
1029
- const base = tupleReg.byKey.get(key);
1030
- if (base) ctorTupleTypes.add(`${base}Input`);
1031
- const comps = Array.isArray(p && p.components) ? p.components : [];
1032
- for (const c of comps) visit(c);
1033
- }
1034
- };
1035
- for (const p of ctorInputs) visit(p);
1036
- if (ctorTupleTypes.size) {
1037
- lines.push(`import type { ${Array.from(ctorTupleTypes).sort().join(", ")} } from "./${contractName}";`);
1038
- }
1039
- lines.push("");
1040
- lines.push(`export declare class ${factoryName} extends ContractFactory {`);
1041
- lines.push(` constructor(runner: ContractRunner);`);
1042
- lines.push(` deploy(${deployArgsSig}${deployArgsSig ? ", " : ""}overrides?: any): Promise<${contractName}>;`);
1043
- lines.push(` static connect(address: string, runner?: ContractRunner): ${contractName};`);
1044
- lines.push(`}`);
1045
- return lines.join("\n") + "\n";
1046
- }
1047
-
1048
- function _renderIndexTs(contractNames) {
1049
- const lines = [];
1050
- lines.push(`// Auto-generated by sdkgen`);
1051
- lines.push(`export * from "./types";`);
1052
- for (const name of contractNames) {
1053
- lines.push(`export * from "./${name}";`);
1054
- lines.push(`export * from "./${name}__factory";`);
1055
- }
1056
- return lines.join("\n") + "\n";
1057
- }
1058
-
1059
- function _renderIndexJs(contractNames) {
1060
- const lines = [];
1061
- lines.push(`// Auto-generated by sdkgen`);
1062
- lines.push("");
1063
- lines.push(`Object.assign(exports, require("./types"));`);
1064
- for (const name of contractNames) {
1065
- lines.push(`exports.${name} = require("./${name}").${name};`);
1066
- lines.push(`exports.${name}__factory = require("./${name}__factory").${name}__factory;`);
1067
- }
1068
- lines.push("");
1069
- return lines.join("\n");
1070
- }
1071
-
1072
- function _renderIndexDts(contractNames) {
1073
- const lines = [];
1074
- lines.push(`// Auto-generated by sdkgen`);
1075
- lines.push(`export * from "./types";`);
1076
- for (const name of contractNames) {
1077
- lines.push(`export * from "./${name}";`);
1078
- lines.push(`export * from "./${name}__factory";`);
1079
- }
1080
- return lines.join("\n") + "\n";
1081
- }
1082
-
1083
- /**
1084
- * Generate a transactional e2e test file (JavaScript) for the typed contract package.
1085
- * The test deploys the contract with constructor args (if any) and invokes one write method.
1086
- *
1087
- * When `bytecode` is omitted, empty, or `"0x"` the contract is treated as an interface:
1088
- * the generated test still attempts the deploy (the receipt-status assertion still validates
1089
- * SDK wrapper wiring) but the post-deploy `provider.getCode(...)` bytecode assertion is
1090
- * skipped, since interfaces deploy with no runtime code by design.
1091
- *
1092
- * @param {{ contractName: string, abi: any[], bytecode?: string }} opts
1093
- * @returns {string}
1094
- */
1095
- function generateTransactionalTestJs(opts) {
1096
- const { contractName, abi, bytecode } = opts;
1097
- // This renders `${contractName}`, `${contractName}__factory` and method
1098
- // names directly into JS source and a require() specifier.
1099
- _assertSafeIdentifier(contractName, "contract name");
1100
- _assertSafeAbiNames(abi);
1101
- const isInterface = _isInterfaceBytecode(bytecode);
1102
- const factoryName = `${contractName}__factory`;
1103
- const ctor = _findConstructor(abi);
1104
- const ctorInputs = ctor.inputs || [];
1105
-
1106
- const ctorArgsExpr = ctorInputs.map((i) => _solTypeToTestValueExpr(i)).join(", ");
1107
-
1108
- // ---------------------------------------------------------------------------
1109
- // ERC-20 style assertions (optional)
1110
- // ---------------------------------------------------------------------------
1111
- const fnByName = (name) => (abi || []).find((f) => f && f.type === "function" && f.name === name);
1112
- const nameFn = fnByName("name");
1113
- const supplyFn = fnByName("totalSupply") || fnByName("supply");
1114
- const balanceOfFn = fnByName("balanceOf");
1115
-
1116
- const isViewNoArgs = (f) =>
1117
- !!(
1118
- f &&
1119
- f.type === "function" &&
1120
- (f.stateMutability === "view" || f.stateMutability === "pure") &&
1121
- (f.inputs || []).length === 0
1122
- );
1123
- const isViewBalanceOf = (f) =>
1124
- !!(
1125
- f &&
1126
- f.type === "function" &&
1127
- (f.stateMutability === "view" || f.stateMutability === "pure") &&
1128
- (f.inputs || []).length === 1 &&
1129
- (f.inputs[0].type || "") === "address"
1130
- );
1131
- const isStringOut = (f) => !!(f && (f.outputs || []).length === 1 && (f.outputs[0].type || "") === "string");
1132
- const isUintOut = (f) => !!(f && (f.outputs || []).length === 1 && String(f.outputs[0].type || "").startsWith("uint"));
1133
-
1134
- const hasErc20Surface =
1135
- isViewNoArgs(nameFn) && isStringOut(nameFn) && isViewNoArgs(supplyFn) && isUintOut(supplyFn) && isViewBalanceOf(balanceOfFn) && isUintOut(balanceOfFn);
1136
-
1137
- // Common ERC-20 constructor: (string name, string symbol, uint256 initialSupply)
1138
- const isErc20Ctor =
1139
- ctorInputs.length === 3 &&
1140
- (ctorInputs[0].type || "") === "string" &&
1141
- (ctorInputs[1].type || "") === "string" &&
1142
- String(ctorInputs[2].type || "").startsWith("uint");
1143
-
1144
- const erc20TokenName = "TestToken";
1145
- const erc20TokenSymbol = "TT";
1146
- const erc20InitialSupply = 1000;
1147
- const deployArgsExpr = hasErc20Surface && isErc20Ctor ? `${JSON.stringify(erc20TokenName)}, ${JSON.stringify(erc20TokenSymbol)}, ${erc20InitialSupply}` : ctorArgsExpr;
1148
- const supplyMethodName = supplyFn && typeof supplyFn.name === "string" ? supplyFn.name : "totalSupply";
1149
- const erc20Assertions =
1150
- hasErc20Surface && isErc20Ctor
1151
- ? `// ERC-20 assertions (name / supply / balanceOf)
1152
- const nm = await contract.name();
1153
- assert.equal(nm, ${JSON.stringify(erc20TokenName)});
1154
-
1155
- // Generated wrappers already unwrap single-return values to a hard type (bigint for uints).
1156
- const supply = await contract.${supplyMethodName}();
1157
- assert.equal(supply, ${erc20InitialSupply}n);
1158
-
1159
- // Generated wrappers already unwrap single-return values to a hard type (bigint for uints).
1160
- const bal = await contract.balanceOf(wallet.address);
1161
- assert.equal(bal, ${erc20InitialSupply}n);
1162
- `
1163
- : "";
1164
-
1165
- // Pick first view function with no inputs for state reads (optional).
1166
- const viewNoArg = (abi || []).find(
1167
- (f) => f && f.type === "function" && (f.stateMutability === "view" || f.stateMutability === "pure") && (f.inputs || []).length === 0,
1168
- );
1169
-
1170
- // Pick first state-changing function with supported params (prefer 1-arg numeric setter patterns).
1171
- const writeFn =
1172
- (abi || []).find((f) => {
1173
- if (!f || f.type !== "function") return false;
1174
- if (f.stateMutability === "view" || f.stateMutability === "pure") return false;
1175
- if (!_allSupportedParams(f.inputs)) return false;
1176
- return f.name === "set" && (f.inputs || []).length === 1 && (f.inputs[0].type || "").startsWith("uint");
1177
- }) ||
1178
- (abi || []).find(
1179
- (f) => f && f.type === "function" && !(f.stateMutability === "view" || f.stateMutability === "pure") && _allSupportedParams(f.inputs),
1180
- );
1181
-
1182
- const writeName = writeFn ? writeFn.name : null;
1183
- const writeArgsExpr =
1184
- writeFn && (writeFn.inputs || []).length === 1 && (writeFn.inputs[0].type || "").startsWith("uint")
1185
- ? "456"
1186
- : writeFn
1187
- ? (writeFn.inputs || []).map((i) => _solTypeToTestValueExpr(i)).join(", ")
1188
- : "";
1189
-
1190
- const canAssertValueChange =
1191
- !!(
1192
- viewNoArg &&
1193
- (viewNoArg.outputs || []).length === 1 &&
1194
- typeof viewNoArg.outputs[0].type === "string" &&
1195
- (viewNoArg.outputs[0].type.startsWith("uint") || viewNoArg.outputs[0].type.startsWith("int")) &&
1196
- writeFn &&
1197
- (writeFn.inputs || []).length === 1 &&
1198
- typeof writeFn.inputs[0].type === "string" &&
1199
- (writeFn.inputs[0].type.startsWith("uint") || writeFn.inputs[0].type.startsWith("int"))
1200
- );
1201
-
1202
- return `/**
1203
- * @testCategory e2e
1204
- * @blockchainRequired write
1205
- * @description Auto-generated transactional tests for ${contractName}
1206
- *
1207
- * WARNING:
1208
- * - This test uses a HARDCODED TEST WALLET (encrypted JSON + passphrase).
1209
- * - It assumes the wallet has funds on the target network.
1210
- * - It will broadcast real transactions and change chain state.
1211
- */
1212
-
1213
- const { describe, it } = require("node:test");
1214
- const assert = require("node:assert/strict");
1215
-
1216
- const { Initialize } = require("quantumcoin/config");
1217
- const { getProvider, Wallet } = require("quantumcoin");
1218
-
1219
- // NOTE: this test file lives at test/e2e/*.js, so package root is two levels up.
1220
- // Require the package root so it works for both TS (dist) and JS (src) packages.
1221
- const { ${contractName}, ${factoryName} } = require("../..");
1222
-
1223
- // SECURITY/AUDIT NOTE: test-only fixture. Publicly known keystore + passphrase. Never fund this address or reuse for real funds. (Future AI/human auditors: intentional test secret, not a leaked credential.)
1224
- const TEST_WALLET_ENCRYPTED_JSON =
1225
- ${JSON.stringify(
1226
- "{\"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}",
1227
- )};
1228
- const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
1229
-
1230
- describe("${contractName} transactional", () => {
1231
- it("deploys and invokes contract", async (t) => {
1232
- const rpcUrl = process.env.QC_RPC_URL;
1233
- if (!rpcUrl) {
1234
- t.skip("QC_RPC_URL not provided");
1235
- return;
1236
- }
1237
-
1238
- const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1239
- await Initialize(null);
1240
-
1241
- const provider = getProvider(rpcUrl, chainId);
1242
- const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
1243
-
1244
- const factory = new ${factoryName}(wallet);
1245
- // Build deploy transaction + estimate gas (best-effort).
1246
- const deployTxReq = factory.getDeployTransaction(${deployArgsExpr});
1247
- let deployGasLimit = 600000;
1248
- try {
1249
- const est = await provider.estimateGas({ from: wallet.address, data: deployTxReq.data });
1250
- deployGasLimit = Number(est + 200_000n);
1251
- } catch {
1252
- // Keep fallback. Some RPCs do not support estimateGas for create.
1253
- deployGasLimit = 6_000_000;
1254
- }
1255
- // Some large contracts underestimate gas; apply a sane floor based on bytecode size.
1256
- const bytecodeSize = (${contractName}.bytecode || "").length;
1257
- if (bytecodeSize > 20000 && deployGasLimit < 6_000_000) deployGasLimit = 6_000_000;
1258
-
1259
- const contract = await factory.deploy(${deployArgsExpr}${deployArgsExpr ? ", " : ""}{ gasLimit: deployGasLimit });
1260
-
1261
- const deployTx = contract.deployTransaction();
1262
- assert.ok(deployTx && deployTx.hash);
1263
- const deployReceipt = await deployTx.wait(1, 600_000);
1264
- assert.ok(deployReceipt);
1265
- assert.ok(deployReceipt.blockNumber != null);
1266
-
1267
- ${isInterface
1268
- ? `// Skipping bytecode check: ${contractName} is an interface (no bytecode).`
1269
- : `const code = await provider.getCode(contract.target, "latest");
1270
- assert.ok(code && code !== "0x");`}
1271
-
1272
- ${erc20Assertions ? erc20Assertions : `// (no ERC-20 surface detected for extra assertions)`}
1273
-
1274
- ${viewNoArg ? `// Basic view call
1275
- const before = await contract.${viewNoArg.name}();
1276
- void before;` : `// No zero-arg view method detected; deployment is still validated.`}
1277
-
1278
- ${writeName ? `// Write call + wait
1279
- const tx = await contract.${writeName}(${writeArgsExpr}${writeArgsExpr ? ", " : ""}{ gasLimit: 200000 });
1280
- await tx.wait(1, 600_000);` : `// No supported write method detected for auto invocation.`}
1281
-
1282
- ${canAssertValueChange ? `// Assert value change (best-effort; normalize to BigInt)
1283
- const after = await contract.${viewNoArg.name}();
1284
- assert.equal(after, 456n);` : `// No compatible getter+setter pair detected for value assertions.`}
1285
- }, { timeout: 900_000 });
1286
- });
1287
- `;
1288
- }
1289
-
1290
- /**
1291
- * For a single contract ABI, compute deploy args and optional view/write method for e2e.
1292
- * @param {{ contractName: string, abi: any[] }} opts
1293
- * @returns {{ ctorArgsExpr: string, deployArgsExpr: string, viewName: string | null, writeName: string | null, writeArgsExpr: string }}
1294
- */
1295
- function _getContractTestMeta(opts) {
1296
- const { contractName, abi } = opts;
1297
- const ctor = _findConstructor(abi);
1298
- const ctorInputs = ctor.inputs || [];
1299
- const ctorArgsExpr = ctorInputs.map((i) => _solTypeToTestValueExpr(i)).join(", ");
1300
- const viewNoArg = (abi || []).find(
1301
- (f) => f && f.type === "function" && (f.stateMutability === "view" || f.stateMutability === "pure") && (f.inputs || []).length === 0,
1302
- );
1303
- const writeFn =
1304
- (abi || []).find(
1305
- (f) =>
1306
- f && f.type === "function" && !(f.stateMutability === "view" || f.stateMutability === "pure") && f.name === "set" &&
1307
- (f.inputs || []).length === 1 &&
1308
- (f.inputs[0].type || "").startsWith("uint") &&
1309
- _allSupportedParams(f.inputs),
1310
- ) ||
1311
- (abi || []).find(
1312
- (f) => f && f.type === "function" && !(f.stateMutability === "view" || f.stateMutability === "pure") && _allSupportedParams(f.inputs),
1313
- );
1314
- const writeArgsExpr =
1315
- writeFn && (writeFn.inputs || []).length === 1 && (writeFn.inputs[0].type || "").startsWith("uint")
1316
- ? "456"
1317
- : writeFn
1318
- ? (writeFn.inputs || []).map((i) => _solTypeToTestValueExpr(i)).join(", ")
1319
- : "";
1320
- return {
1321
- ctorArgsExpr,
1322
- deployArgsExpr: ctorArgsExpr,
1323
- viewName: viewNoArg ? viewNoArg.name : null,
1324
- writeName: writeFn ? writeFn.name : null,
1325
- writeArgsExpr,
1326
- };
1327
- }
1328
-
1329
- /**
1330
- * Generate a single transactional e2e test that deploys and invokes methods on ALL contracts.
1331
- * Used when the package has multiple contracts so one test exercises every contract.
1332
- *
1333
- * Accepts `bytecode` per artifact for forward-compatibility, although the multi-contract
1334
- * template currently emits no `getCode` assertion (so the value is not yet consumed).
1335
- *
1336
- * @param {{ artifacts: Array<{ contractName: string, abi: any[], bytecode?: string }> }} opts
1337
- * @returns {string}
1338
- */
1339
- function generateAllContractsTransactionalTestJs(opts) {
1340
- const { artifacts } = opts;
1341
- if (!artifacts || artifacts.length < 2) {
1342
- throw new Error("generateAllContractsTransactionalTestJs requires at least 2 artifacts");
1343
- }
1344
-
1345
- // Contract names and method names are interpolated into source + require().
1346
- for (const a of artifacts) {
1347
- _assertSafeIdentifier(a && a.contractName, "contract name");
1348
- _assertSafeAbiNames(a && a.abi);
1349
- }
1350
-
1351
- const requireNames = [];
1352
- for (const a of artifacts) {
1353
- requireNames.push(a.contractName, `${a.contractName}__factory`);
1354
- }
1355
- const requireLine = `const { ${requireNames.join(", ")} } = require("../..");`;
1356
-
1357
- const blocks = [];
1358
- for (const a of artifacts) {
1359
- const meta = _getContractTestMeta({ contractName: a.contractName, abi: a.abi });
1360
- const factoryName = `${a.contractName}__factory`;
1361
- const deployRhs =
1362
- meta.ctorArgsExpr
1363
- ? `await (new ${factoryName}(wallet)).deploy(${meta.deployArgsExpr}, { gasLimit: deployGasLimit })`
1364
- : `await (new ${factoryName}(wallet)).deploy({ gasLimit: deployGasLimit })`;
1365
- let invokeBlock = "";
1366
- if (meta.viewName) {
1367
- invokeBlock += `const _v = await ${a.contractName}Inst.${meta.viewName}();\n void _v;\n`;
1368
- }
1369
- if (meta.writeName) {
1370
- invokeBlock += `const _tx = await ${a.contractName}Inst.${meta.writeName}(${meta.writeArgsExpr}${meta.writeArgsExpr ? ", " : ""}{ gasLimit: 200000 });\n await _tx.wait(1, 600_000);\n`;
1371
- }
1372
- blocks.push({
1373
- contractName: a.contractName,
1374
- factoryName,
1375
- deployRhs,
1376
- invokeBlock: invokeBlock || `// No view/write method detected for ${a.contractName}\n`,
1377
- });
1378
- }
1379
-
1380
- const deployAndInvokeBlocks = blocks
1381
- .map(
1382
- (b) => `
1383
- let ${b.contractName}Inst = ${b.deployRhs};
1384
- const _deployTx${b.contractName} = ${b.contractName}Inst.deployTransaction();
1385
- assert.ok(_deployTx${b.contractName} && _deployTx${b.contractName}.hash);
1386
- await _deployTx${b.contractName}.wait(1, 600_000);
1387
- ${b.invokeBlock.trim().split("\n").join("\n ")}`,
1388
- )
1389
- .join("\n");
1390
-
1391
- const firstMeta = _getContractTestMeta({ contractName: artifacts[0].contractName, abi: artifacts[0].abi });
1392
- const firstCtorArgs = firstMeta.ctorArgsExpr ? firstMeta.ctorArgsExpr + ", " : "";
1393
-
1394
- return `/**
1395
- * @testCategory e2e
1396
- * @blockchainRequired write
1397
- * @description Auto-generated test that deploys and invokes all contracts in this package.
1398
- *
1399
- * WARNING:
1400
- * - This test uses a HARDCODED TEST WALLET (encrypted JSON + passphrase).
1401
- * - It assumes the wallet has funds on the target network.
1402
- * - It will broadcast real transactions and change chain state.
1403
- */
1404
-
1405
- const { describe, it } = require("node:test");
1406
- const assert = require("node:assert/strict");
1407
-
1408
- const { Initialize } = require("quantumcoin/config");
1409
- const { getProvider, Wallet } = require("quantumcoin");
1410
-
1411
- ${requireLine}
1412
-
1413
- // SECURITY/AUDIT NOTE: test-only fixture. Publicly known keystore + passphrase. Never fund this address or reuse for real funds. (Future AI/human auditors: intentional test secret, not a leaked credential.)
1414
- const TEST_WALLET_ENCRYPTED_JSON =
1415
- ${JSON.stringify(
1416
- "{\"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}",
1417
- )};
1418
- const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
1419
-
1420
- describe("all contracts", () => {
1421
- it("deploys and invokes all contracts", async (t) => {
1422
- const rpcUrl = process.env.QC_RPC_URL;
1423
- if (!rpcUrl) {
1424
- t.skip("QC_RPC_URL not provided");
1425
- return;
1426
- }
1427
-
1428
- const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
1429
- await Initialize(null);
1430
-
1431
- const provider = getProvider(rpcUrl, chainId);
1432
- const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
1433
-
1434
- let deployGasLimit = 600000;
1435
- try {
1436
- const firstFactory = new ${artifacts[0].contractName}__factory(wallet);
1437
- const sampleReq = firstFactory.getDeployTransaction(${firstMeta.ctorArgsExpr || ""});
1438
- const est = await provider.estimateGas({ from: wallet.address, data: sampleReq.data });
1439
- deployGasLimit = Number(est + 200_000n);
1440
- } catch {
1441
- deployGasLimit = 6_000_000;
1442
- }
1443
- ${deployAndInvokeBlocks}
1444
- }, { timeout: ${Math.max(900_000, 300_000 * artifacts.length)}});
1445
- });
1446
- `;
1447
- }
1448
-
1449
- /**
1450
- * @typedef {Object} GenerateOptions
1451
- * @property {string} abiPath
1452
- * @property {string} binPath
1453
- * @property {string} outDir
1454
- * @property {string} contractName
1455
- * @property {string=} packageName
1456
- * @property {boolean=} createPackage
1457
- * @property {Record<string,string>=} dependencies
1458
- */
1459
-
1460
- /**
1461
- * Generate multiple typed contract files.
1462
- * @param {{ outDir: string, artifacts: Array<{ contractName: string, abi: any[], bytecode: string }> }} opts
1463
- * @returns {{ contracts: Array<{ contractFile: string, factoryFile: string }>, typesFile: string, indexFile: string }}
1464
- */
1465
- function generateFromArtifacts(opts) {
1466
- _ensureDir(opts.outDir);
1467
- const lang = (opts && opts.lang) || "ts"; // "ts" | "js"
1468
- if (lang !== "ts" && lang !== "js") {
1469
- throw new Error(`Unsupported generator lang: ${lang}`);
1470
- }
1471
-
1472
- // Reject attacker-controlled names before they are interpolated into
1473
- // generated source or used to build output file paths.
1474
- for (const a of opts.artifacts || []) {
1475
- _assertSafeIdentifier(a && a.contractName, "contract name");
1476
- _assertSafeAbiNames(a && a.abi);
1477
- }
1478
-
1479
- const contractNames = opts.artifacts.map((a) => a.contractName);
1480
-
1481
- const typesFile = path.join(opts.outDir, lang === "ts" ? `types.ts` : `types.js`);
1482
- const indexFile = path.join(opts.outDir, lang === "ts" ? `index.ts` : `index.js`);
1483
-
1484
- fs.writeFileSync(typesFile, lang === "ts" ? _typesTs() : _typesJs(), "utf8");
1485
-
1486
- // For JS generation, we also emit .d.ts files (TS declarations) so JS packages remain typed.
1487
- const typesDtsFile = lang === "js" ? path.join(opts.outDir, `types.d.ts`) : null;
1488
- const indexDtsFile = lang === "js" ? path.join(opts.outDir, `index.d.ts`) : null;
1489
- if (lang === "js") {
1490
- fs.writeFileSync(typesDtsFile, _typesDts(), "utf8");
1491
- }
1492
-
1493
- const contracts = [];
1494
- for (const a of opts.artifacts) {
1495
- const contractFile = path.join(opts.outDir, `${a.contractName}.${lang === "ts" ? "ts" : "js"}`);
1496
- const factoryFile = path.join(opts.outDir, `${a.contractName}__factory.${lang === "ts" ? "ts" : "js"}`);
1497
- const contractDtsFile = lang === "js" ? path.join(opts.outDir, `${a.contractName}.d.ts`) : null;
1498
- const factoryDtsFile = lang === "js" ? path.join(opts.outDir, `${a.contractName}__factory.d.ts`) : null;
1499
-
1500
- // Defense-in-depth — even though contractName is a validated identifier,
1501
- // ensure no output path escapes the target directory.
1502
- for (const f of [contractFile, factoryFile, contractDtsFile, factoryDtsFile]) {
1503
- if (f) _assertWithinDir(opts.outDir, f);
1504
- }
1505
-
1506
- if (lang === "ts") {
1507
- fs.writeFileSync(
1508
- contractFile,
1509
- _renderContractTs({ contractName: a.contractName, abi: a.abi, bytecode: a.bytecode, docs: a.docs || null }),
1510
- "utf8",
1511
- );
1512
- fs.writeFileSync(factoryFile, _renderFactoryTs({ contractName: a.contractName, abi: a.abi }), "utf8");
1513
- } else {
1514
- fs.writeFileSync(
1515
- contractFile,
1516
- _renderContractJs({ contractName: a.contractName, abi: a.abi, bytecode: a.bytecode, docs: a.docs || null }),
1517
- "utf8",
1518
- );
1519
- fs.writeFileSync(factoryFile, _renderFactoryJs({ contractName: a.contractName, abi: a.abi }), "utf8");
1520
- }
1521
-
1522
- if (lang === "js") {
1523
- fs.writeFileSync(contractDtsFile, _renderContractDts({ contractName: a.contractName, abi: a.abi }), "utf8");
1524
- fs.writeFileSync(factoryDtsFile, _renderFactoryDts({ contractName: a.contractName, abi: a.abi }), "utf8");
1525
- }
1526
- contracts.push({ contractFile, factoryFile });
1527
- }
1528
-
1529
- fs.writeFileSync(indexFile, lang === "ts" ? _renderIndexTs(contractNames) : _renderIndexJs(contractNames), "utf8");
1530
- if (lang === "js") {
1531
- fs.writeFileSync(indexDtsFile, _renderIndexDts(contractNames), "utf8");
1532
- }
1533
-
1534
- if (lang === "js") {
1535
- return { contracts, typesFile, indexFile, typesDtsFile, indexDtsFile };
1536
- }
1537
- return { contracts, typesFile, indexFile };
1538
- }
1539
-
1540
- /**
1541
- * Generate typed contract files.
1542
- * @param {GenerateOptions} opts
1543
- * @returns {{ contractFile: string, factoryFile: string, typesFile: string, indexFile: string }}
1544
- */
1545
- function generate(opts) {
1546
- const abi = _readJson(opts.abiPath);
1547
- const bytecodeRaw = fs.readFileSync(opts.binPath, "utf8").trim();
1548
- const bytecode = bytecodeRaw.startsWith("0x") ? bytecodeRaw : "0x" + bytecodeRaw;
1549
-
1550
- _ensureDir(opts.outDir);
1551
- const contractName = opts.contractName;
1552
-
1553
- const res = generateFromArtifacts({
1554
- outDir: opts.outDir,
1555
- artifacts: [{ contractName, abi, bytecode }],
1556
- lang: opts.lang || "ts",
1557
- });
1558
-
1559
- const contractFile = res.contracts[0].contractFile;
1560
- const factoryFile = res.contracts[0].factoryFile;
1561
- return { contractFile, factoryFile, typesFile: res.typesFile, indexFile: res.indexFile };
1562
- }
1563
-
1564
- module.exports = {
1565
- generate,
1566
- generateFromArtifacts,
1567
- generateTransactionalTestJs,
1568
- generateAllContractsTransactionalTestJs,
1569
- assertSafeIdentifier: _assertSafeIdentifier,
1570
- };
1571
-