quantum-coin-js-sdk 2.0.1 → 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 (232) hide show
  1. package/README.md +13 -0
  2. package/index.d.ts +9 -0
  3. package/index.js +37 -4
  4. package/package.json +12 -4
  5. package/wasmBase64.js +3 -2
  6. package/.idea/.name +0 -1
  7. package/.idea/modules.xml +0 -8
  8. package/.idea/quantum-coin-js-sdk.iml +0 -12
  9. package/.idea/vcs.xml +0 -6
  10. package/.vs/ProjectSettings.json +0 -3
  11. package/.vs/VSWorkspaceState.json +0 -8
  12. package/.vs/quantum-coin-js-sdk/FileContentIndex/a9d6de82-cfc3-4c22-ac38-06d706ff3673.vsidx +0 -0
  13. package/.vs/quantum-coin-js-sdk/FileContentIndex/fdc816df-1898-4153-99f2-c69ef4c2283b.vsidx +0 -0
  14. package/.vs/quantum-coin-js-sdk/config/applicationhost.config +0 -1011
  15. package/.vs/quantum-coin-js-sdk/v17/.wsuo +0 -0
  16. package/.vs/quantum-coin-js-sdk/v17/DocumentLayout.backup.json +0 -39
  17. package/.vs/quantum-coin-js-sdk/v17/DocumentLayout.json +0 -57
  18. package/.vs/slnx.sqlite +0 -0
  19. package/example/README.md +0 -14
  20. package/example/conversion-example.js +0 -19
  21. package/example/example-create-contract.js +0 -396
  22. package/example/example-encode-decode-rlp.js +0 -225
  23. package/example/example-event-pack-unpack.js +0 -391
  24. package/example/example-misc.js +0 -96
  25. package/example/example-token-pack-unpack.js +0 -960
  26. package/example/example-wallet-version4.js +0 -34
  27. package/example/example-wallet.js +0 -39
  28. package/example/example.js +0 -115
  29. package/example/node_modules/.bin/sdkgen +0 -16
  30. package/example/node_modules/.bin/sdkgen.cmd +0 -17
  31. package/example/node_modules/.bin/sdkgen.ps1 +0 -28
  32. package/example/node_modules/.package-lock.json +0 -51
  33. package/example/node_modules/quantumcoin/.github/workflows/publish-npmjs.yaml +0 -22
  34. package/example/node_modules/quantumcoin/LICENSE +0 -21
  35. package/example/node_modules/quantumcoin/README-SDK.md +0 -828
  36. package/example/node_modules/quantumcoin/README.md +0 -197
  37. package/example/node_modules/quantumcoin/config.d.ts +0 -50
  38. package/example/node_modules/quantumcoin/config.js +0 -123
  39. package/example/node_modules/quantumcoin/examples/AllSolidityTypes.sol +0 -184
  40. package/example/node_modules/quantumcoin/examples/SimpleIERC20.sol +0 -74
  41. package/example/node_modules/quantumcoin/examples/events.js +0 -41
  42. package/example/node_modules/quantumcoin/examples/events.ts +0 -35
  43. package/example/node_modules/quantumcoin/examples/example-generator-sdk-js.js +0 -100
  44. package/example/node_modules/quantumcoin/examples/example-generator-sdk-js.ts +0 -77
  45. package/example/node_modules/quantumcoin/examples/example-generator-sdk-ts.js +0 -100
  46. package/example/node_modules/quantumcoin/examples/example-generator-sdk-ts.ts +0 -77
  47. package/example/node_modules/quantumcoin/examples/example.js +0 -67
  48. package/example/node_modules/quantumcoin/examples/example.ts +0 -56
  49. package/example/node_modules/quantumcoin/examples/offline-signing.js +0 -77
  50. package/example/node_modules/quantumcoin/examples/offline-signing.ts +0 -65
  51. package/example/node_modules/quantumcoin/examples/package-lock.json +0 -596
  52. package/example/node_modules/quantumcoin/examples/package.json +0 -32
  53. package/example/node_modules/quantumcoin/examples/read-operations.js +0 -32
  54. package/example/node_modules/quantumcoin/examples/read-operations.ts +0 -31
  55. package/example/node_modules/quantumcoin/examples/sdk-generator-erc20.inline.json +0 -251
  56. package/example/node_modules/quantumcoin/examples/solidity-types.ts +0 -43
  57. package/example/node_modules/quantumcoin/examples/wallet-offline.js +0 -36
  58. package/example/node_modules/quantumcoin/examples/wallet-offline.ts +0 -26
  59. package/example/node_modules/quantumcoin/generate-sdk.js +0 -1846
  60. package/example/node_modules/quantumcoin/index.js +0 -12
  61. package/example/node_modules/quantumcoin/package.json +0 -95
  62. package/example/node_modules/quantumcoin/scripts/copy-declarations.js +0 -31
  63. package/example/node_modules/quantumcoin/scripts/run-all-one-by-one.js +0 -151
  64. package/example/node_modules/quantumcoin/src/abi/fragments.d.ts +0 -42
  65. package/example/node_modules/quantumcoin/src/abi/fragments.js +0 -63
  66. package/example/node_modules/quantumcoin/src/abi/index.d.ts +0 -13
  67. package/example/node_modules/quantumcoin/src/abi/index.js +0 -9
  68. package/example/node_modules/quantumcoin/src/abi/interface.d.ts +0 -128
  69. package/example/node_modules/quantumcoin/src/abi/interface.js +0 -605
  70. package/example/node_modules/quantumcoin/src/abi/js-abi-coder.d.ts +0 -8
  71. package/example/node_modules/quantumcoin/src/abi/js-abi-coder.js +0 -538
  72. package/example/node_modules/quantumcoin/src/constants.d.ts +0 -61
  73. package/example/node_modules/quantumcoin/src/constants.js +0 -94
  74. package/example/node_modules/quantumcoin/src/contract/contract-factory.d.ts +0 -28
  75. package/example/node_modules/quantumcoin/src/contract/contract-factory.js +0 -111
  76. package/example/node_modules/quantumcoin/src/contract/contract.d.ts +0 -113
  77. package/example/node_modules/quantumcoin/src/contract/contract.js +0 -408
  78. package/example/node_modules/quantumcoin/src/contract/index.d.ts +0 -9
  79. package/example/node_modules/quantumcoin/src/contract/index.js +0 -9
  80. package/example/node_modules/quantumcoin/src/errors/index.d.ts +0 -92
  81. package/example/node_modules/quantumcoin/src/errors/index.js +0 -200
  82. package/example/node_modules/quantumcoin/src/generator/index.d.ts +0 -81
  83. package/example/node_modules/quantumcoin/src/generator/index.js +0 -1571
  84. package/example/node_modules/quantumcoin/src/index.d.ts +0 -122
  85. package/example/node_modules/quantumcoin/src/index.js +0 -41
  86. package/example/node_modules/quantumcoin/src/internal/hex.d.ts +0 -68
  87. package/example/node_modules/quantumcoin/src/internal/hex.js +0 -180
  88. package/example/node_modules/quantumcoin/src/providers/extra-providers.d.ts +0 -139
  89. package/example/node_modules/quantumcoin/src/providers/extra-providers.js +0 -614
  90. package/example/node_modules/quantumcoin/src/providers/index.d.ts +0 -17
  91. package/example/node_modules/quantumcoin/src/providers/index.js +0 -10
  92. package/example/node_modules/quantumcoin/src/providers/json-rpc-provider.d.ts +0 -12
  93. package/example/node_modules/quantumcoin/src/providers/json-rpc-provider.js +0 -89
  94. package/example/node_modules/quantumcoin/src/providers/provider.d.ts +0 -206
  95. package/example/node_modules/quantumcoin/src/providers/provider.js +0 -524
  96. package/example/node_modules/quantumcoin/src/types/index.d.ts +0 -214
  97. package/example/node_modules/quantumcoin/src/types/index.js +0 -9
  98. package/example/node_modules/quantumcoin/src/utils/address.d.ts +0 -58
  99. package/example/node_modules/quantumcoin/src/utils/address.js +0 -144
  100. package/example/node_modules/quantumcoin/src/utils/encoding.d.ts +0 -120
  101. package/example/node_modules/quantumcoin/src/utils/encoding.js +0 -306
  102. package/example/node_modules/quantumcoin/src/utils/fixednumber.d.ts +0 -57
  103. package/example/node_modules/quantumcoin/src/utils/fixednumber.js +0 -366
  104. package/example/node_modules/quantumcoin/src/utils/hashing.d.ts +0 -76
  105. package/example/node_modules/quantumcoin/src/utils/hashing.js +0 -298
  106. package/example/node_modules/quantumcoin/src/utils/index.d.ts +0 -63
  107. package/example/node_modules/quantumcoin/src/utils/index.js +0 -14
  108. package/example/node_modules/quantumcoin/src/utils/result.d.ts +0 -57
  109. package/example/node_modules/quantumcoin/src/utils/result.js +0 -128
  110. package/example/node_modules/quantumcoin/src/utils/rlp.d.ts +0 -12
  111. package/example/node_modules/quantumcoin/src/utils/rlp.js +0 -215
  112. package/example/node_modules/quantumcoin/src/utils/units.d.ts +0 -29
  113. package/example/node_modules/quantumcoin/src/utils/units.js +0 -107
  114. package/example/node_modules/quantumcoin/src/wallet/index.d.ts +0 -10
  115. package/example/node_modules/quantumcoin/src/wallet/index.js +0 -8
  116. package/example/node_modules/quantumcoin/src/wallet/wallet.d.ts +0 -192
  117. package/example/node_modules/quantumcoin/src/wallet/wallet.js +0 -713
  118. package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.dynamic.test.js +0 -207
  119. package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.dynamic.test.ts +0 -191
  120. package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.fixtures.js +0 -231
  121. package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.generated-sdks.e2e.test.js +0 -387
  122. package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.generated-sdks.e2e.test.ts +0 -350
  123. package/example/node_modules/quantumcoin/test/e2e/generator-interface.e2e.test.js +0 -165
  124. package/example/node_modules/quantumcoin/test/e2e/generator-interface.e2e.test.ts +0 -160
  125. package/example/node_modules/quantumcoin/test/e2e/helpers.js +0 -59
  126. package/example/node_modules/quantumcoin/test/e2e/signing-context-and-fee.e2e.test.js +0 -141
  127. package/example/node_modules/quantumcoin/test/e2e/signing-context-and-fee.e2e.test.ts +0 -128
  128. package/example/node_modules/quantumcoin/test/e2e/simple-erc20.generated-sdks.e2e.test.js +0 -168
  129. package/example/node_modules/quantumcoin/test/e2e/simple-erc20.generated-sdks.e2e.test.ts +0 -141
  130. package/example/node_modules/quantumcoin/test/e2e/transactional.test.js +0 -245
  131. package/example/node_modules/quantumcoin/test/e2e/transactional.test.ts +0 -208
  132. package/example/node_modules/quantumcoin/test/e2e/typed-generator.e2e.test.js +0 -407
  133. package/example/node_modules/quantumcoin/test/e2e/typed-generator.e2e.test.ts +0 -337
  134. package/example/node_modules/quantumcoin/test/fixtures/ConstructorParam.sol +0 -23
  135. package/example/node_modules/quantumcoin/test/fixtures/MultiContracts.sol +0 -37
  136. package/example/node_modules/quantumcoin/test/fixtures/SimpleStorage.sol +0 -18
  137. package/example/node_modules/quantumcoin/test/fixtures/StakingContract.abi.json +0 -1
  138. package/example/node_modules/quantumcoin/test/integration/ipc-provider.test.js +0 -49
  139. package/example/node_modules/quantumcoin/test/integration/ipc-provider.test.ts +0 -44
  140. package/example/node_modules/quantumcoin/test/integration/provider.test.js +0 -88
  141. package/example/node_modules/quantumcoin/test/integration/provider.test.ts +0 -85
  142. package/example/node_modules/quantumcoin/test/integration/ws-provider.test.js +0 -41
  143. package/example/node_modules/quantumcoin/test/integration/ws-provider.test.ts +0 -38
  144. package/example/node_modules/quantumcoin/test/security/abi-decoder-bounds.test.js +0 -122
  145. package/example/node_modules/quantumcoin/test/security/contract-overrides.test.js +0 -112
  146. package/example/node_modules/quantumcoin/test/security/generator-injection.test.js +0 -195
  147. package/example/node_modules/quantumcoin/test/security/malformed-input.test.js +0 -330
  148. package/example/node_modules/quantumcoin/test/security/malformed-input.test.ts +0 -35
  149. package/example/node_modules/quantumcoin/test/security/rpc-numeric-bounds.test.js +0 -81
  150. package/example/node_modules/quantumcoin/test/security/rpc-trust.test.js +0 -202
  151. package/example/node_modules/quantumcoin/test/unit/_encrypted-output.txt +0 -6
  152. package/example/node_modules/quantumcoin/test/unit/_log-encrypted-jsons.js +0 -45
  153. package/example/node_modules/quantumcoin/test/unit/_write-keystore-fixture.js +0 -16
  154. package/example/node_modules/quantumcoin/test/unit/abi-interface.test.js +0 -110
  155. package/example/node_modules/quantumcoin/test/unit/abi-interface.test.ts +0 -109
  156. package/example/node_modules/quantumcoin/test/unit/address-wallet.test.js +0 -923
  157. package/example/node_modules/quantumcoin/test/unit/address-wallet.test.ts +0 -877
  158. package/example/node_modules/quantumcoin/test/unit/browser-provider.test.js +0 -85
  159. package/example/node_modules/quantumcoin/test/unit/browser-provider.test.ts +0 -79
  160. package/example/node_modules/quantumcoin/test/unit/contract.test.js +0 -85
  161. package/example/node_modules/quantumcoin/test/unit/contract.test.ts +0 -83
  162. package/example/node_modules/quantumcoin/test/unit/encoding-units-rlp.test.js +0 -127
  163. package/example/node_modules/quantumcoin/test/unit/encoding-units-rlp.test.ts +0 -91
  164. package/example/node_modules/quantumcoin/test/unit/errors.test.js +0 -77
  165. package/example/node_modules/quantumcoin/test/unit/errors.test.ts +0 -76
  166. package/example/node_modules/quantumcoin/test/unit/filter-by-blockhash.test.js +0 -55
  167. package/example/node_modules/quantumcoin/test/unit/filter-by-blockhash.test.ts +0 -54
  168. package/example/node_modules/quantumcoin/test/unit/fixednumber.test.js +0 -656
  169. package/example/node_modules/quantumcoin/test/unit/fixednumber.test.ts +0 -660
  170. package/example/node_modules/quantumcoin/test/unit/fixtures/encrypted-keystores-48-32-36.js +0 -9
  171. package/example/node_modules/quantumcoin/test/unit/generate-contract-cli.test.js +0 -42
  172. package/example/node_modules/quantumcoin/test/unit/generate-contract-cli.test.ts +0 -41
  173. package/example/node_modules/quantumcoin/test/unit/generate-sdk-artifacts-json.test.js +0 -113
  174. package/example/node_modules/quantumcoin/test/unit/generate-sdk-artifacts-json.test.ts +0 -110
  175. package/example/node_modules/quantumcoin/test/unit/generator.test.js +0 -149
  176. package/example/node_modules/quantumcoin/test/unit/generator.test.ts +0 -148
  177. package/example/node_modules/quantumcoin/test/unit/hashing.test.js +0 -64
  178. package/example/node_modules/quantumcoin/test/unit/hashing.test.ts +0 -63
  179. package/example/node_modules/quantumcoin/test/unit/init.test.js +0 -39
  180. package/example/node_modules/quantumcoin/test/unit/init.test.ts +0 -38
  181. package/example/node_modules/quantumcoin/test/unit/interface.test.js +0 -56
  182. package/example/node_modules/quantumcoin/test/unit/interface.test.ts +0 -54
  183. package/example/node_modules/quantumcoin/test/unit/internal-hex.test.js +0 -81
  184. package/example/node_modules/quantumcoin/test/unit/internal-hex.test.ts +0 -80
  185. package/example/node_modules/quantumcoin/test/unit/populate-transaction.test.js +0 -98
  186. package/example/node_modules/quantumcoin/test/unit/populate-transaction.test.ts +0 -64
  187. package/example/node_modules/quantumcoin/test/unit/providers.test.js +0 -252
  188. package/example/node_modules/quantumcoin/test/unit/providers.test.ts +0 -251
  189. package/example/node_modules/quantumcoin/test/unit/result.test.js +0 -80
  190. package/example/node_modules/quantumcoin/test/unit/result.test.ts +0 -79
  191. package/example/node_modules/quantumcoin/test/unit/solidity-types.test.js +0 -49
  192. package/example/node_modules/quantumcoin/test/unit/solidity-types.test.ts +0 -39
  193. package/example/node_modules/quantumcoin/test/unit/utils.test.js +0 -57
  194. package/example/node_modules/quantumcoin/test/unit/utils.test.ts +0 -56
  195. package/example/node_modules/quantumcoin/test/verbose-logger.js +0 -74
  196. package/example/node_modules/quantumcoin/tsconfig.build.json +0 -14
  197. package/example/node_modules/seed-words/.github/workflows/publish-npmjs.yaml +0 -22
  198. package/example/node_modules/seed-words/BUILD.md +0 -7
  199. package/example/node_modules/seed-words/LICENSE +0 -121
  200. package/example/node_modules/seed-words/README.md +0 -67
  201. package/example/node_modules/seed-words/dist/seedwords.d.ts +0 -39
  202. package/example/node_modules/seed-words/package.json +0 -27
  203. package/example/node_modules/seed-words/seedwords.js +0 -315
  204. package/example/node_modules/seed-words/seedwords.txt +0 -65536
  205. package/example/node_modules/seed-words/tsconfig.json +0 -21
  206. package/example/package-lock.json +0 -60
  207. package/example/package.json +0 -15
  208. package/scripts/build-browser-tests.js +0 -29
  209. package/scripts/build-wasm.js +0 -85
  210. package/tests/browser/bundle.js +0 -97643
  211. package/tests/browser/entry.js +0 -57
  212. package/tests/browser/harness.html +0 -20
  213. package/tests/browser/run-browser.js +0 -50
  214. package/tests/encrypted-32.json +0 -1
  215. package/tests/encrypted-36.json +0 -1
  216. package/tests/encrypted-48.json +0 -1
  217. package/tests/generate-verify-vectors.js +0 -88
  218. package/tests/get-gas-price.test.js +0 -59
  219. package/tests/hashing.test.js +0 -34
  220. package/tests/non-transactional.preinit.test.js +0 -33
  221. package/tests/non-transactional.test.js +0 -1318
  222. package/tests/sign-raw-keytype5-context-null.test.js +0 -105
  223. package/tests/sign-raw-transaction.test.js +0 -194
  224. package/tests/sign-verify.test.js +0 -308
  225. package/tests/specs/assert.js +0 -74
  226. package/tests/specs/crypto.spec.js +0 -104
  227. package/tests/specs/hex.js +0 -31
  228. package/tests/specs/preinit.spec.js +0 -19
  229. package/tests/specs/sign.spec.js +0 -90
  230. package/tests/specs/wallet.spec.js +0 -98
  231. package/tests/transactional.rpc.test.js +0 -102
  232. 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
-