quantumcoin 7.0.12 → 7.0.14

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 (214) hide show
  1. package/README-SDK.md +828 -823
  2. package/README.md +4 -0
  3. package/config.d.ts +50 -50
  4. package/examples/example-generated-sdk-js/README.md +65 -0
  5. package/examples/example-generated-sdk-js/examples/_test-wallet.js +17 -0
  6. package/examples/example-generated-sdk-js/examples/deploy.js +41 -0
  7. package/examples/example-generated-sdk-js/examples/events.js +36 -0
  8. package/examples/example-generated-sdk-js/examples/read-operations.js +46 -0
  9. package/examples/example-generated-sdk-js/examples/write-operations.js +44 -0
  10. package/examples/example-generated-sdk-js/index.d.ts +1 -0
  11. package/examples/example-generated-sdk-js/index.js +15 -0
  12. package/examples/example-generated-sdk-js/package-lock.json +59 -0
  13. package/examples/example-generated-sdk-js/package.json +22 -0
  14. package/examples/example-generated-sdk-js/src/SimpleERC20.d.ts +19 -0
  15. package/examples/example-generated-sdk-js/src/SimpleERC20.js +353 -0
  16. package/examples/example-generated-sdk-js/src/SimpleERC20__factory.d.ts +10 -0
  17. package/examples/example-generated-sdk-js/src/SimpleERC20__factory.js +29 -0
  18. package/examples/example-generated-sdk-js/src/index.d.ts +4 -0
  19. package/examples/example-generated-sdk-js/src/index.js +5 -0
  20. package/examples/example-generated-sdk-js/src/quantumcoin-shims.d.ts +23 -0
  21. package/examples/example-generated-sdk-js/src/types.d.ts +3 -0
  22. package/examples/example-generated-sdk-js/src/types.js +3 -0
  23. package/examples/example-generated-sdk-js/test/e2e/SimpleERC20.e2e.test.js +78 -0
  24. package/examples/example-generated-sdk-ts/README.md +65 -0
  25. package/examples/example-generated-sdk-ts/examples/_test-wallet.js +17 -0
  26. package/examples/example-generated-sdk-ts/examples/deploy.js +41 -0
  27. package/examples/example-generated-sdk-ts/examples/events.js +36 -0
  28. package/examples/example-generated-sdk-ts/examples/read-operations.js +46 -0
  29. package/examples/example-generated-sdk-ts/examples/write-operations.js +44 -0
  30. package/examples/example-generated-sdk-ts/index.d.ts +1 -0
  31. package/examples/example-generated-sdk-ts/index.js +15 -0
  32. package/examples/example-generated-sdk-ts/package-lock.json +59 -0
  33. package/examples/example-generated-sdk-ts/package.json +23 -0
  34. package/examples/example-generated-sdk-ts/src/SimpleERC20.ts +334 -0
  35. package/examples/example-generated-sdk-ts/src/SimpleERC20__factory.ts +28 -0
  36. package/examples/example-generated-sdk-ts/src/index.ts +4 -0
  37. package/examples/example-generated-sdk-ts/src/quantumcoin-shims.d.ts +23 -0
  38. package/examples/example-generated-sdk-ts/src/types.ts +4 -0
  39. package/examples/example-generated-sdk-ts/test/e2e/SimpleERC20.e2e.test.js +78 -0
  40. package/examples/example-generated-sdk-ts/tsconfig.json +14 -0
  41. package/examples/node_modules/.package-lock.json +5 -5
  42. package/examples/node_modules/quantum-coin-js-sdk/README.md +5 -5
  43. package/examples/node_modules/quantum-coin-js-sdk/example/package-lock.json +1 -1
  44. package/examples/node_modules/quantum-coin-js-sdk/index.d.ts +1031 -1031
  45. package/examples/node_modules/quantum-coin-js-sdk/index.js +15 -15
  46. package/examples/node_modules/quantum-coin-js-sdk/package.json +1 -1
  47. package/examples/node_modules/quantum-coin-js-sdk/wasmBase64.js +2 -2
  48. package/examples/package-lock.json +6 -6
  49. package/examples/package.json +1 -1
  50. package/examples/sdk-generator-erc20.inline.json +251 -251
  51. package/generate-sdk.js +1845 -1822
  52. package/package.json +2 -2
  53. package/src/abi/fragments.d.ts +42 -42
  54. package/src/abi/index.d.ts +13 -13
  55. package/src/abi/interface.js +11 -2
  56. package/src/abi/js-abi-coder.js +61 -2
  57. package/src/contract/contract.js +53 -5
  58. package/src/contract/index.d.ts +9 -9
  59. package/src/errors/index.d.ts +92 -92
  60. package/src/generator/index.d.ts +11 -4
  61. package/src/generator/index.js +185 -18
  62. package/src/internal/hex.d.ts +68 -61
  63. package/src/internal/hex.js +36 -0
  64. package/src/providers/json-rpc-provider.d.ts +12 -12
  65. package/src/providers/provider.js +141 -8
  66. package/src/utils/address.d.ts +58 -58
  67. package/src/utils/encoding.d.ts +120 -120
  68. package/src/utils/hashing.js +298 -298
  69. package/src/utils/index.d.ts +63 -63
  70. package/src/utils/index.js +14 -14
  71. package/src/utils/result.d.ts +57 -57
  72. package/src/utils/rlp.d.ts +12 -12
  73. package/src/utils/rlp.js +13 -1
  74. package/src/utils/units.d.ts +29 -29
  75. package/src/wallet/index.d.ts +10 -10
  76. package/src/wallet/wallet.d.ts +192 -192
  77. package/src/wallet/wallet.js +713 -630
  78. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/README.md +83 -0
  79. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/artifacts/AllSolidityTypes.abi.json +12544 -0
  80. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/artifacts/AllSolidityTypes.bin +1 -0
  81. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/_test-wallet.js +17 -0
  82. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/_test-wallet.ts +10 -0
  83. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/deploy.js +41 -0
  84. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/deploy.ts +41 -0
  85. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/events.js +36 -0
  86. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/events.ts +36 -0
  87. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/offline-signing.js +82 -0
  88. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/offline-signing.ts +80 -0
  89. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/read-operations.js +46 -0
  90. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/read-operations.ts +44 -0
  91. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/write-operations.js +44 -0
  92. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/write-operations.ts +44 -0
  93. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/index.d.ts +1 -0
  94. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/index.js +21 -0
  95. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/package-lock.json +597 -0
  96. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/package.json +25 -0
  97. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/AllSolidityTypes.d.ts +1280 -0
  98. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/AllSolidityTypes.js +14021 -0
  99. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/AllSolidityTypes__factory.d.ts +11 -0
  100. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/AllSolidityTypes__factory.js +31 -0
  101. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/index.d.ts +4 -0
  102. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/index.js +5 -0
  103. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/quantumcoin-shims.d.ts +25 -0
  104. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/types.d.ts +3 -0
  105. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/types.js +3 -0
  106. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/test/e2e/AllSolidityTypes.e2e.test.js +77 -0
  107. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/test/e2e/AllSolidityTypes.extra.test.js +195 -0
  108. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/README.md +83 -0
  109. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/artifacts/AllSolidityTypes.abi.json +12544 -0
  110. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/artifacts/AllSolidityTypes.bin +1 -0
  111. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/_test-wallet.js +17 -0
  112. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/_test-wallet.ts +10 -0
  113. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/deploy.js +41 -0
  114. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/deploy.ts +41 -0
  115. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/events.js +36 -0
  116. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/events.ts +36 -0
  117. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/offline-signing.js +82 -0
  118. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/offline-signing.ts +80 -0
  119. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/read-operations.js +46 -0
  120. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/read-operations.ts +44 -0
  121. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/write-operations.js +44 -0
  122. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/write-operations.ts +44 -0
  123. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/index.d.ts +1 -0
  124. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/index.js +21 -0
  125. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/package-lock.json +597 -0
  126. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/package.json +26 -0
  127. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/src/AllSolidityTypes.ts +13940 -0
  128. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/src/AllSolidityTypes__factory.ts +31 -0
  129. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/src/index.ts +4 -0
  130. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/src/quantumcoin-shims.d.ts +25 -0
  131. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/src/types.ts +4 -0
  132. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/test/e2e/AllSolidityTypes.e2e.test.js +77 -0
  133. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/test/e2e/AllSolidityTypes.extra.test.js +195 -0
  134. package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/tsconfig.json +18 -0
  135. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/README.md +74 -0
  136. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/artifacts/SimpleERC20.abi.json +245 -0
  137. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/artifacts/SimpleERC20.bin +1 -0
  138. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/_test-wallet.js +17 -0
  139. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/_test-wallet.ts +10 -0
  140. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/deploy.js +41 -0
  141. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/deploy.ts +41 -0
  142. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/events.js +36 -0
  143. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/events.ts +36 -0
  144. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/offline-signing.js +82 -0
  145. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/offline-signing.ts +80 -0
  146. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/read-operations.js +46 -0
  147. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/read-operations.ts +44 -0
  148. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/write-operations.js +44 -0
  149. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/write-operations.ts +44 -0
  150. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/index.d.ts +1 -0
  151. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/index.js +16 -0
  152. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/package-lock.json +597 -0
  153. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/package.json +25 -0
  154. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/SimpleERC20.d.ts +24 -0
  155. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/SimpleERC20.js +378 -0
  156. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/SimpleERC20__factory.d.ts +10 -0
  157. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/SimpleERC20__factory.js +31 -0
  158. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/index.d.ts +4 -0
  159. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/index.js +5 -0
  160. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/quantumcoin-shims.d.ts +25 -0
  161. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/types.d.ts +3 -0
  162. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/types.js +3 -0
  163. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/test/e2e/SimpleERC20.e2e.test.js +90 -0
  164. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/README.md +74 -0
  165. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/artifacts/SimpleERC20.abi.json +245 -0
  166. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/artifacts/SimpleERC20.bin +1 -0
  167. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/_test-wallet.js +17 -0
  168. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/_test-wallet.ts +10 -0
  169. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/deploy.js +41 -0
  170. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/deploy.ts +41 -0
  171. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/events.js +36 -0
  172. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/events.ts +36 -0
  173. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/offline-signing.js +82 -0
  174. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/offline-signing.ts +80 -0
  175. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/read-operations.js +46 -0
  176. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/read-operations.ts +44 -0
  177. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/write-operations.js +44 -0
  178. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/write-operations.ts +44 -0
  179. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/index.d.ts +1 -0
  180. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/index.js +16 -0
  181. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/package-lock.json +597 -0
  182. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/package.json +26 -0
  183. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/SimpleERC20.ts +361 -0
  184. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/SimpleERC20__factory.ts +30 -0
  185. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/index.ts +4 -0
  186. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/quantumcoin-shims.d.ts +25 -0
  187. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/types.ts +4 -0
  188. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/test/e2e/SimpleERC20.e2e.test.js +90 -0
  189. package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/tsconfig.json +18 -0
  190. package/test/e2e/generator-interface.e2e.test.js +165 -0
  191. package/test/e2e/generator-interface.e2e.test.ts +160 -0
  192. package/test/e2e/signing-context-and-fee.e2e.test.js +141 -141
  193. package/test/e2e/signing-context-and-fee.e2e.test.ts +128 -128
  194. package/test/integration/provider.test.js +88 -88
  195. package/test/security/abi-decoder-bounds.test.js +122 -0
  196. package/test/security/contract-overrides.test.js +112 -0
  197. package/test/security/generator-injection.test.js +195 -0
  198. package/test/security/malformed-input.test.js +26 -27
  199. package/test/security/rpc-numeric-bounds.test.js +81 -0
  200. package/test/security/rpc-trust.test.js +202 -0
  201. package/test/unit/abi-interface.test.js +12 -5
  202. package/test/unit/abi-interface.test.ts +8 -1
  203. package/test/unit/address-wallet.test.js +923 -892
  204. package/test/unit/address-wallet.test.ts +877 -877
  205. package/test/unit/encoding-units-rlp.test.js +35 -0
  206. package/test/unit/generator.test.js +48 -1
  207. package/test/unit/generator.test.ts +48 -1
  208. package/test/unit/hashing.test.js +64 -64
  209. package/test/unit/hashing.test.ts +63 -63
  210. package/test/unit/internal-hex.test.js +32 -1
  211. package/test/unit/internal-hex.test.ts +32 -1
  212. package/test/unit/populate-transaction.test.js +33 -0
  213. package/test/unit/providers.test.js +51 -1
  214. package/test/unit/providers.test.ts +53 -0
@@ -1,630 +1,713 @@
1
- /**
2
- * @fileoverview Wallet and signer implementations.
3
- *
4
- * The QuantumCoin.js wallet model mirrors ethers.js v6:
5
- * - AbstractSigner -> BaseWallet -> Wallet
6
- * - NonceManager wrapper
7
- *
8
- * Cryptographic operations are delegated to `quantum-coin-js-sdk`.
9
- */
10
-
11
- const qcsdk = require("quantum-coin-js-sdk");
12
- const seedWords = require("seed-words");
13
- const { JsonRpcProvider } = require("../providers/json-rpc-provider");
14
- const { assertArgument, assertSecretArgument, makeError } = require("../errors");
15
- const { arrayify, bytesToHex, hexToBytes, isHexString, normalizeHex } = require("../internal/hex");
16
- const { getAddress } = require("../utils/address");
17
- const { WeiPerEther } = require("../constants");
18
-
19
- function _requireInitialized() {
20
- // eslint-disable-next-line global-require
21
- const { isInitialized, getInitializationPromise } = require("../../config");
22
- if (isInitialized()) return;
23
- if (getInitializationPromise() != null) {
24
- throw makeError(
25
- "QuantumCoin SDK is still initializing. Await the Initialize() promise before using SDK methods.",
26
- "UNKNOWN_ERROR",
27
- { operation: "requireInitialized" },
28
- );
29
- }
30
- throw makeError("QuantumCoin SDK not initialized. Call Initialize() first.", "UNKNOWN_ERROR", { operation: "wallet" });
31
- }
32
-
33
- function _bytesToNumberArray(bytes) {
34
- return Array.from(bytes);
35
- }
36
-
37
- const _maxSafeInt = 0x1fffffffffffffn; // 2^53 - 1
38
-
39
- function _getBigInt(value, name) {
40
- if (typeof value === "bigint") return value;
41
- if (typeof value === "number") {
42
- assertArgument(Number.isInteger(value), "underflow", name, value);
43
- assertArgument(value >= -Number(_maxSafeInt) && value <= Number(_maxSafeInt), "overflow", name, value);
44
- return BigInt(value);
45
- }
46
- if (typeof value === "string") {
47
- if (value === "0x" || value === "0X") return 0n;
48
- try { return BigInt(value); }
49
- catch { assertArgument(false, "invalid BigNumberish string", name, value); }
50
- }
51
- assertArgument(false, "invalid BigNumberish", name, value);
52
- }
53
-
54
- function _getNumber(value, name) {
55
- const bi = _getBigInt(value, name);
56
- assertArgument(bi >= -_maxSafeInt && bi <= _maxSafeInt, "overflow", name, value);
57
- return Number(bi);
58
- }
59
-
60
- /**
61
- * SigningKey wrapper (PQC private/public key bytes).
62
- */
63
- class SigningKey {
64
- /**
65
- * @param {Uint8Array} privateKeyBytes
66
- * @param {Uint8Array} publicKeyBytes
67
- */
68
- constructor(privateKeyBytes, publicKeyBytes) {
69
- Object.defineProperty(this, "privateKeyBytes", {
70
- enumerable: false,
71
- configurable: false,
72
- writable: false,
73
- value: new Uint8Array(privateKeyBytes),
74
- });
75
- this.publicKeyBytes = new Uint8Array(publicKeyBytes);
76
- }
77
-
78
- toJSON() {
79
- return {};
80
- }
81
- }
82
-
83
- /**
84
- * AbstractSigner base (minimal).
85
- */
86
- class AbstractSigner {
87
- /**
88
- * @param {import("../providers/provider").AbstractProvider|null} provider
89
- */
90
- constructor(provider) {
91
- this.provider = provider || null;
92
- }
93
-
94
- async getAddress() {
95
- throw makeError("getAddress not implemented", "NOT_IMPLEMENTED", {});
96
- }
97
- }
98
-
99
- /**
100
- * BaseWallet - core signing implementation.
101
- */
102
- class BaseWallet extends AbstractSigner {
103
- /**
104
- * @param {SigningKey} signingKey
105
- * @param {import("../providers/provider").AbstractProvider|null=} provider
106
- * @param {{ address: string }=} precomputed
107
- * @param {any=} qcWallet Internal quantum-coin-js-sdk Wallet (optional)
108
- */
109
- constructor(signingKey, provider, precomputed, qcWallet) {
110
- super(provider || null);
111
- _requireInitialized();
112
-
113
- Object.defineProperty(this, "signingKey", {
114
- enumerable: false,
115
- configurable: false,
116
- writable: false,
117
- value: signingKey,
118
- });
119
- Object.defineProperty(this, "_qcWallet", {
120
- enumerable: false,
121
- configurable: true,
122
- writable: true,
123
- value: qcWallet || null,
124
- });
125
-
126
- /** @type {string} */
127
- this.address = precomputed?.address || "";
128
-
129
- Object.defineProperty(this, "privateKey", {
130
- enumerable: false,
131
- get: () => bytesToHex(this.signingKey.privateKeyBytes),
132
- });
133
-
134
- Object.defineProperty(this, "publicKey", {
135
- enumerable: true,
136
- get: () => bytesToHex(this.signingKey.publicKeyBytes),
137
- });
138
-
139
- Object.defineProperty(this, "_seed", {
140
- enumerable: false,
141
- configurable: true,
142
- writable: true,
143
- value: null,
144
- });
145
-
146
- Object.defineProperty(this, "seed", {
147
- enumerable: false,
148
- get: () => this._seed,
149
- });
150
- }
151
-
152
- toJSON() {
153
- return { address: this.address };
154
- }
155
-
156
- async getAddress() {
157
- return this.address;
158
- }
159
-
160
- /**
161
- * Sign a transaction using quantum-coin-js-sdk signRawTransaction().
162
- * @param {import("../providers/provider").TransactionRequest} tx
163
- * @returns {Promise<string>}
164
- */
165
- async signTransaction(tx) {
166
- _requireInitialized();
167
- assertArgument(tx && typeof tx === "object", "invalid tx", "tx", tx);
168
-
169
- const toAddress = tx.to == null ? null : getAddress(tx.to);
170
- const valueInWei = tx.value == null ? 0n : _getBigInt(tx.value, "tx.value");
171
- const gasLimit = tx.gasLimit == null ? 21000 : _getNumber(tx.gasLimit, "tx.gasLimit");
172
-
173
- const data = tx.data == null ? null : normalizeHex(tx.data);
174
- const remarks = tx.remarks == null ? null : normalizeHex(tx.remarks);
175
-
176
- if (remarks != null) {
177
- assertArgument(isHexString(remarks), "remarks must be hex string", "remarks", remarks);
178
- const remarkBytes = hexToBytes(remarks);
179
- assertArgument(remarkBytes.length <= 32, "remarks too long (max 32 bytes)", "remarks", remarks);
180
- }
181
-
182
- // Nonce must be provided or resolved from provider.
183
- let nonce = tx.nonce;
184
- if (nonce == null) {
185
- if (!this.provider) throw makeError("missing provider to resolve nonce", "UNKNOWN_ERROR", { operation: "signTransaction" });
186
- // Prefer pending to avoid nonce collisions with in-flight transactions.
187
- try {
188
- nonce = await this.provider.getTransactionCount(this.address, "pending");
189
- } catch {
190
- nonce = await this.provider.getTransactionCount(this.address, "latest");
191
- }
192
- }
193
- assertArgument(Number.isInteger(nonce) && nonce >= 0, "invalid nonce", "nonce", nonce);
194
-
195
- const chainId = tx.chainId != null ? tx.chainId : (this.provider && this.provider.chainId != null ? this.provider.chainId : null);
196
- const signingContext = tx.signingContext ?? null;
197
-
198
- /** @type {any} */
199
- const qcWallet =
200
- this._qcWallet ||
201
- new qcsdk.Wallet(
202
- this.address,
203
- _bytesToNumberArray(this.signingKey.privateKeyBytes),
204
- _bytesToNumberArray(this.signingKey.publicKeyBytes),
205
- );
206
-
207
- const req = new qcsdk.TransactionSigningRequest(
208
- qcWallet,
209
- toAddress,
210
- valueInWei,
211
- nonce,
212
- data,
213
- gasLimit,
214
- remarks,
215
- chainId,
216
- signingContext,
217
- );
218
- const signResult = qcsdk.signRawTransaction(req);
219
- // quantum-coin-js-sdk returns a SignResult object: { resultCode, txnHash, txnData }
220
- if (!signResult || typeof signResult !== "object") {
221
- throw makeError("signRawTransaction failed", "UNKNOWN_ERROR", {});
222
- }
223
- if (typeof signResult.resultCode === "number" && signResult.resultCode !== 0) {
224
- throw makeError("signRawTransaction failed", "UNKNOWN_ERROR", {
225
- resultCode: signResult.resultCode,
226
- hash: signResult.txnHash || null,
227
- });
228
- }
229
- const raw = signResult.txnData;
230
- if (typeof raw !== "string") throw makeError("signRawTransaction did not return txnData", "UNKNOWN_ERROR", {});
231
- return normalizeHex(raw);
232
- }
233
-
234
- /**
235
- * Signs and sends a transaction.
236
- * @param {import("../providers/provider").TransactionRequest} tx
237
- * @returns {Promise<import("../providers/provider").TransactionResponse>}
238
- */
239
- async sendTransaction(tx) {
240
- if (!this.provider) throw makeError("missing provider", "UNKNOWN_ERROR", { operation: "sendTransaction" });
241
- const raw = await this.signTransaction({ ...tx, from: this.address });
242
- return this.provider.sendTransaction(raw);
243
- }
244
- }
245
-
246
- /**
247
- * Wallet - convenience methods around BaseWallet.
248
- */
249
- class Wallet extends BaseWallet {
250
- /**
251
- * @param {string|Uint8Array|SigningKey} key
252
- * @param {import("../providers/provider").AbstractProvider=} provider
253
- */
254
- constructor(key, provider) {
255
- _requireInitialized();
256
-
257
- let signingKey;
258
- let qcAddress;
259
-
260
- if (key instanceof SigningKey) {
261
- signingKey = key;
262
- // Compute address from public key
263
- const addr = qcsdk.addressFromPublicKey(_bytesToNumberArray(signingKey.publicKeyBytes));
264
- if (typeof addr !== "string") throw makeError("addressFromPublicKey failed", "UNKNOWN_ERROR", {});
265
- qcAddress = normalizeHex(addr);
266
- } else {
267
- const privBytes = typeof key === "string" ? hexToBytes(key) : arrayify(key);
268
- const pubHex = qcsdk.publicKeyFromPrivateKey(_bytesToNumberArray(privBytes));
269
- if (typeof pubHex !== "string") throw makeError("publicKeyFromPrivateKey failed", "UNKNOWN_ERROR", {});
270
- const pubBytes = hexToBytes(pubHex);
271
- const addr = qcsdk.addressFromPublicKey(_bytesToNumberArray(pubBytes));
272
- if (typeof addr !== "string") throw makeError("addressFromPublicKey failed", "UNKNOWN_ERROR", {});
273
- qcAddress = normalizeHex(addr);
274
- signingKey = new SigningKey(privBytes, pubBytes);
275
- }
276
-
277
- /** @type {any} */
278
- const qcWallet = new qcsdk.Wallet(
279
- qcAddress,
280
- _bytesToNumberArray(signingKey.privateKeyBytes),
281
- _bytesToNumberArray(signingKey.publicKeyBytes),
282
- );
283
-
284
- super(signingKey, provider || null, { address: qcAddress }, qcWallet);
285
- }
286
-
287
- /**
288
- * Returns wallet address (sync).
289
- * @returns {string}
290
- */
291
- getAddress() {
292
- return this.address;
293
- }
294
-
295
- /**
296
- * Returns wallet balance.
297
- * @param {string=} blockTag
298
- * @returns {Promise<bigint>}
299
- */
300
- async getBalance(blockTag) {
301
- if (!this.provider) throw makeError("missing provider", "UNKNOWN_ERROR", { operation: "getBalance" });
302
- void blockTag;
303
- return this.provider.getBalance(this.address);
304
- }
305
-
306
- /**
307
- * Returns wallet nonce.
308
- * @param {string=} blockTag
309
- * @returns {Promise<number>}
310
- */
311
- async getTransactionCount(blockTag) {
312
- if (!this.provider) throw makeError("missing provider", "UNKNOWN_ERROR", { operation: "getTransactionCount" });
313
- return this.provider.getTransactionCount(this.address, blockTag);
314
- }
315
-
316
- /**
317
- * Encrypts and serializes this wallet to JSON.
318
- * @param {string|Uint8Array} password
319
- * @returns {string}
320
- */
321
- encryptSync(password) {
322
- _requireInitialized();
323
- if (this._seed != null) {
324
- return Wallet.encryptSeedSync(hexToBytes(this._seed), password);
325
- }
326
- const pw = typeof password === "string"
327
- ? password.normalize("NFC")
328
- : Buffer.from(arrayify(password)).toString("utf8").normalize("NFC");
329
- assertSecretArgument(pw.length >= 12, "password must be at least 12 characters", "password");
330
- const json = qcsdk.serializeEncryptedWallet(this._qcWallet, pw);
331
- if (typeof json !== "string") throw makeError("serializeEncryptedWallet failed", "UNKNOWN_ERROR", {});
332
- return json;
333
- }
334
-
335
- /**
336
- * Encrypts raw seed bytes into a wallet JSON string (version 5 pre-expansion format).
337
- * The resulting JSON can be opened with `Wallet.fromEncryptedJsonSync()` or
338
- * Desktop/Mobile/Web/CLI wallet applications.
339
- * @param {number[]|Uint8Array} seed Raw seed bytes: 64 (keyType 3), 72 (keyType 5), or 96 (legacy)
340
- * @param {string|Uint8Array} password Passphrase (at least 12 characters)
341
- * @returns {string}
342
- */
343
- static encryptSeedSync(seed, password) {
344
- _requireInitialized();
345
- const seedArr = seed instanceof Uint8Array ? Array.from(seed) : seed;
346
- assertArgument(Array.isArray(seedArr), "seed must be an array of numbers or Uint8Array", "seed", seed);
347
- const allowedLengths = [64, 72, 96];
348
- assertArgument(allowedLengths.includes(seedArr.length), "seed must be 64, 72, or 96 bytes", "seed", seedArr.length);
349
- const pw = typeof password === "string"
350
- ? password.normalize("NFC")
351
- : Buffer.from(arrayify(password)).toString("utf8").normalize("NFC");
352
- assertSecretArgument(pw.length >= 12, "password must be at least 12 characters", "password");
353
- const json = qcsdk.serializeSeedAsEncryptedWallet(seedArr, pw);
354
- if (typeof json !== "string") throw makeError("serializeSeedAsEncryptedWallet failed", "UNKNOWN_ERROR", {});
355
- return json;
356
- }
357
-
358
- /**
359
- * Returns the seed phrase (list of words) if this wallet has a seed, else null.
360
- * Derived from the stored pre-expansion seed via seed-words.getWordListFromSeedArray.
361
- *
362
- * Non-null for wallets created via createRandom, fromPhrase, fromSeed, and
363
- * fromEncryptedJsonSync when the JSON is a version-5 keystore produced by
364
- * encryptSync on a seed-bearing wallet or by encryptSeedSync.
365
- * Null for fromKeys and for v3/v4 keystores without preExpansionSeed.
366
- *
367
- * @returns {string[]|null}
368
- */
369
- getPhrase() {
370
- _requireInitialized();
371
- if (this._seed == null) return null;
372
- const bytes = Array.from(hexToBytes(this._seed));
373
- const words = seedWords.getWordListFromSeedArray(bytes);
374
- return Array.isArray(words) ? words : null;
375
- }
376
-
377
- /**
378
- * Returns the recommended signing context for this wallet.
379
- * Setting fullSign to true may incur additional gas cost.
380
- * @param {boolean|null=} fullSign Defaults to false when null or omitted.
381
- * @returns {number}
382
- */
383
- getSigningContext(fullSign) {
384
- const fs = fullSign ?? false;
385
- const pubLen = this.signingKey.publicKeyBytes.length;
386
- if (pubLen === 1408) {
387
- return fs ? 2 : 0;
388
- }
389
- if (pubLen === 2688) {
390
- return 1;
391
- }
392
- throw makeError("unsupported public key size", "UNSUPPORTED_OPERATION", { publicKeyLength: pubLen });
393
- }
394
-
395
- /**
396
- * Returns a new wallet connected to a provider.
397
- * @param {import("../providers/provider").AbstractProvider} provider
398
- * @returns {Wallet}
399
- */
400
- connect(provider) {
401
- const w = new Wallet(this.signingKey, provider);
402
- w._qcWallet = this._qcWallet;
403
- w._seed = this._seed;
404
- return w;
405
- }
406
-
407
- /**
408
- * Creates a new random wallet.
409
- * @param {import("../providers/provider").AbstractProvider=} provider
410
- * @param {number|null=} keyType Optional key type: null (default=3), 3, or 5
411
- * @returns {Wallet}
412
- */
413
- static createRandom(provider, keyType) {
414
- _requireInitialized();
415
- if (keyType != null) {
416
- assertArgument(keyType === 3 || keyType === 5, "keyType must be null, 3, or 5", "keyType", keyType);
417
- }
418
- const words = qcsdk.newWalletSeedWords(keyType ?? null);
419
- if (!words || !Array.isArray(words)) {
420
- throw makeError("newWalletSeedWords failed", "UNKNOWN_ERROR", { result: words });
421
- }
422
- return Wallet.fromPhrase(words, provider);
423
- }
424
-
425
- /**
426
- * Creates a wallet from raw seed bytes.
427
- * @param {number[]} seed Raw seed bytes: 64 (keyType 3), 72 (keyType 5), or 96 (legacy)
428
- * @param {import("../providers/provider").AbstractProvider=} provider
429
- * @returns {Wallet}
430
- */
431
- static fromSeed(seed, provider) {
432
- _requireInitialized();
433
- assertArgument(Array.isArray(seed), "seed must be an array of numbers", "seed", seed);
434
- const allowedLengths = [64, 72, 96];
435
- assertArgument(allowedLengths.includes(seed.length), "seed must be 64, 72, or 96 bytes", "seed", seed.length);
436
- const qcWallet = qcsdk.openWalletFromSeed(seed);
437
- if (!qcWallet || typeof qcWallet === "number") {
438
- throw makeError("openWalletFromSeed failed", "UNKNOWN_ERROR", { result: qcWallet });
439
- }
440
- return Wallet._fromQcWallet(qcWallet, provider || null);
441
- }
442
-
443
- /**
444
- * Creates a wallet from an encrypted JSON string.
445
- * @param {string} json
446
- * @param {string} password
447
- * @param {import("../providers/provider").AbstractProvider=} provider
448
- * @returns {Wallet}
449
- */
450
- static fromEncryptedJsonSync(json, password, provider) {
451
- _requireInitialized();
452
- const qcWallet = qcsdk.deserializeEncryptedWallet(json, password);
453
- if (!qcWallet) throw makeError("deserializeEncryptedWallet failed", "UNKNOWN_ERROR", {});
454
- return Wallet._fromQcWallet(qcWallet, provider || null);
455
- }
456
-
457
- /**
458
- * Creates a wallet from a seed phrase (32, 36, or 48 words).
459
- * @param {string|string[]} phrase
460
- * @param {import("../providers/provider").AbstractProvider=} provider
461
- * @returns {Wallet}
462
- */
463
- static fromPhrase(phrase, provider) {
464
- _requireInitialized();
465
- let words = phrase;
466
- if (typeof phrase === "string") {
467
- words = phrase
468
- .split(/[,\s]+/g)
469
- .map((w) => w.trim())
470
- .filter(Boolean);
471
- }
472
- assertArgument(Array.isArray(words), "phrase must be a string or string[]", "phrase", phrase);
473
- const allowedLengths = [32, 36, 48];
474
- assertArgument(
475
- allowedLengths.includes(words.length),
476
- "seed phrase must contain 32, 36, or 48 words",
477
- "phrase",
478
- words.length,
479
- );
480
- const qcWallet = qcsdk.openWalletFromSeedWords(words);
481
- if (!qcWallet) throw makeError("openWalletFromSeedWords failed", "UNKNOWN_ERROR", {});
482
- return Wallet._fromQcWallet(qcWallet, provider || null);
483
- }
484
-
485
- /**
486
- * Creates a wallet from raw private and public key bytes.
487
- * @param {Uint8Array|string} privateKey Private key bytes or hex string
488
- * @param {Uint8Array|string} publicKey Public key bytes or hex string
489
- * @param {import("../providers/provider").AbstractProvider=} provider
490
- * @returns {Wallet}
491
- */
492
- static fromKeys(privateKey, publicKey, provider) {
493
- _requireInitialized();
494
- const privBytes = typeof privateKey === "string" ? hexToBytes(privateKey) : arrayify(privateKey);
495
- const pubBytes = typeof publicKey === "string" ? hexToBytes(publicKey) : arrayify(publicKey);
496
- assertSecretArgument(privBytes.length > 0, "privateKey must not be empty", "privateKey");
497
- assertSecretArgument(pubBytes.length > 0, "publicKey must not be empty", "publicKey");
498
-
499
- const privArr = _bytesToNumberArray(privBytes);
500
- const pubArr = _bytesToNumberArray(pubBytes);
501
- const addr = qcsdk.addressFromPublicKey(pubArr);
502
- if (typeof addr !== "string") throw makeError("addressFromPublicKey failed", "UNKNOWN_ERROR", {});
503
-
504
- const qcWallet = new qcsdk.Wallet(addr, privArr, pubArr);
505
- const verified = qcsdk.verifyWallet(qcWallet);
506
- if (verified !== true) {
507
- throw makeError("verifyWallet failed: the provided key pair is invalid", "INVALID_ARGUMENT", { verified });
508
- }
509
-
510
- return Wallet._fromQcWallet(qcWallet, provider || null);
511
- }
512
-
513
- /**
514
- * Internal helper: build a Wallet from a quantum-coin-js-sdk Wallet object.
515
- * @param {any} qcWallet
516
- * @param {import("../providers/provider").AbstractProvider|null} provider
517
- * @returns {Wallet}
518
- */
519
- static _fromQcWallet(qcWallet, provider) {
520
- const privSrc = qcWallet.privateKey;
521
- const pubSrc = qcWallet.publicKey;
522
-
523
- if (!privSrc || (privSrc instanceof Uint8Array && privSrc.length === 0) || (Array.isArray(privSrc) && privSrc.length === 0)) {
524
- throw makeError("qcWallet.privateKey is empty or missing", "UNKNOWN_ERROR", {});
525
- }
526
- if (!pubSrc || (pubSrc instanceof Uint8Array && pubSrc.length === 0) || (Array.isArray(pubSrc) && pubSrc.length === 0)) {
527
- throw makeError("qcWallet.publicKey is empty or missing", "UNKNOWN_ERROR", {});
528
- }
529
-
530
- const privBytes =
531
- privSrc instanceof Uint8Array ? new Uint8Array(privSrc) : Uint8Array.from(Array.from(privSrc).map((n) => (Number(n) & 0xff)));
532
- const pubBytes =
533
- pubSrc instanceof Uint8Array ? new Uint8Array(pubSrc) : Uint8Array.from(Array.from(pubSrc).map((n) => (Number(n) & 0xff)));
534
- const key = new SigningKey(privBytes, pubBytes);
535
-
536
- const w = new Wallet(key, provider || null);
537
- // Ensure we keep the exact underlying qcWallet for operations like encrypt/signRawTransaction.
538
- w._qcWallet = qcWallet;
539
- if (typeof qcWallet.address === "string") {
540
- w.address = normalizeHex(qcWallet.address);
541
- }
542
- if (qcWallet.preExpansionSeed != null) {
543
- const seedSrc = qcWallet.preExpansionSeed;
544
- const seedBytes =
545
- seedSrc instanceof Uint8Array ? seedSrc : Uint8Array.from(Array.from(seedSrc).map((n) => (Number(n) & 0xff)));
546
- w._seed = bytesToHex(seedBytes);
547
- }
548
- return w;
549
- }
550
- }
551
-
552
- /**
553
- * NonceManager wrapper.
554
- */
555
- class NonceManager extends AbstractSigner {
556
- /**
557
- * @param {AbstractSigner} signer
558
- */
559
- constructor(signer) {
560
- super(signer.provider || null);
561
- this.signer = signer;
562
- this._nonce = null;
563
- }
564
-
565
- async getAddress() {
566
- return this.signer.getAddress();
567
- }
568
-
569
- async getTransactionCount(blockTag) {
570
- if (this._nonce != null) return this._nonce;
571
- if (!this.provider) throw makeError("missing provider", "UNKNOWN_ERROR", { operation: "getTransactionCount" });
572
- const address = await this.getAddress();
573
- this._nonce = await this.provider.getTransactionCount(address, blockTag);
574
- return this._nonce;
575
- }
576
-
577
- async sendTransaction(tx) {
578
- const nonce = await this.getTransactionCount("latest");
579
- const result = await this.signer.sendTransaction({ ...tx, nonce });
580
- this._nonce = nonce + 1;
581
- return result;
582
- }
583
-
584
- reset() {
585
- this._nonce = null;
586
- }
587
-
588
- increment() {
589
- if (this._nonce == null) this._nonce = 0;
590
- this._nonce++;
591
- }
592
- }
593
-
594
- /**
595
- * JsonRpcSigner placeholder (ethers-like).
596
- * This SDK encourages using Wallet directly for signing.
597
- */
598
- class JsonRpcSigner extends AbstractSigner {
599
- constructor(provider, address) {
600
- super(provider);
601
- this._address = address;
602
- }
603
- async getAddress() {
604
- return this._address;
605
- }
606
- }
607
-
608
- /**
609
- * VoidSigner (cannot sign, only provides address).
610
- */
611
- class VoidSigner extends AbstractSigner {
612
- constructor(address, provider) {
613
- super(provider || null);
614
- this._address = getAddress(address);
615
- }
616
- async getAddress() {
617
- return this._address;
618
- }
619
- }
620
-
621
- module.exports = {
622
- SigningKey,
623
- AbstractSigner,
624
- BaseWallet,
625
- Wallet,
626
- NonceManager,
627
- JsonRpcSigner,
628
- VoidSigner,
629
- };
630
-
1
+ /**
2
+ * @fileoverview Wallet and signer implementations.
3
+ *
4
+ * The QuantumCoin.js wallet model mirrors ethers.js v6:
5
+ * - AbstractSigner -> BaseWallet -> Wallet
6
+ * - NonceManager wrapper
7
+ *
8
+ * Cryptographic operations are delegated to `quantum-coin-js-sdk`.
9
+ */
10
+
11
+ const qcsdk = require("quantum-coin-js-sdk");
12
+ const seedWords = require("seed-words");
13
+ const { JsonRpcProvider } = require("../providers/json-rpc-provider");
14
+ const { assertArgument, assertSecretArgument, makeError } = require("../errors");
15
+ const { arrayify, bytesToHex, hexToBytes, isHexString, normalizeHex } = require("../internal/hex");
16
+ const { getAddress } = require("../utils/address");
17
+ const { WeiPerEther } = require("../constants");
18
+
19
+ function _requireInitialized() {
20
+ // eslint-disable-next-line global-require
21
+ const { isInitialized, getInitializationPromise } = require("../../config");
22
+ if (isInitialized()) return;
23
+ if (getInitializationPromise() != null) {
24
+ throw makeError(
25
+ "QuantumCoin SDK is still initializing. Await the Initialize() promise before using SDK methods.",
26
+ "UNKNOWN_ERROR",
27
+ { operation: "requireInitialized" },
28
+ );
29
+ }
30
+ throw makeError("QuantumCoin SDK not initialized. Call Initialize() first.", "UNKNOWN_ERROR", { operation: "wallet" });
31
+ }
32
+
33
+ function _bytesToNumberArray(bytes) {
34
+ return Array.from(bytes);
35
+ }
36
+
37
+ /**
38
+ * Verify that a private/public key pair is internally consistent (the public
39
+ * key really corresponds to the private key). Constructing a wallet from a
40
+ * mismatched pair must fail loudly here rather than silently producing a wallet
41
+ * that signs with one key but claims another.
42
+ *
43
+ * NOTE: `qcsdk.verifyWallet` requires the raw (non-`0x`-prefixed) address form,
44
+ * so we build the verification wallet from `addressFromPublicKey` directly
45
+ * (mirroring the original `fromKeys` flow) rather than reusing a wallet that may
46
+ * carry a normalized `0x` address.
47
+ *
48
+ * @param {Uint8Array} privateKeyBytes
49
+ * @param {Uint8Array} publicKeyBytes
50
+ */
51
+ function _assertKeyPairValid(privateKeyBytes, publicKeyBytes) {
52
+ const privArr = _bytesToNumberArray(privateKeyBytes);
53
+ const pubArr = _bytesToNumberArray(publicKeyBytes);
54
+ const addr = qcsdk.addressFromPublicKey(pubArr);
55
+ if (typeof addr !== "string") throw makeError("addressFromPublicKey failed", "UNKNOWN_ERROR", {});
56
+ const checkWallet = new qcsdk.Wallet(addr, privArr, pubArr);
57
+ const verified = qcsdk.verifyWallet(checkWallet);
58
+ if (verified !== true) {
59
+ throw makeError("verifyWallet failed: the provided key pair is invalid", "INVALID_ARGUMENT", { verified });
60
+ }
61
+ }
62
+
63
+ const _maxSafeInt = 0x1fffffffffffffn; // 2^53 - 1
64
+
65
+ function _getBigInt(value, name) {
66
+ if (typeof value === "bigint") return value;
67
+ if (typeof value === "number") {
68
+ assertArgument(Number.isInteger(value), "underflow", name, value);
69
+ assertArgument(value >= -Number(_maxSafeInt) && value <= Number(_maxSafeInt), "overflow", name, value);
70
+ return BigInt(value);
71
+ }
72
+ if (typeof value === "string") {
73
+ if (value === "0x" || value === "0X") return 0n;
74
+ try { return BigInt(value); }
75
+ catch { assertArgument(false, "invalid BigNumberish string", name, value); }
76
+ }
77
+ assertArgument(false, "invalid BigNumberish", name, value);
78
+ }
79
+
80
+ function _getNumber(value, name) {
81
+ const bi = _getBigInt(value, name);
82
+ assertArgument(bi >= -_maxSafeInt && bi <= _maxSafeInt, "overflow", name, value);
83
+ return Number(bi);
84
+ }
85
+
86
+ /**
87
+ * SigningKey wrapper (PQC private/public key bytes).
88
+ */
89
+ class SigningKey {
90
+ /**
91
+ * @param {Uint8Array} privateKeyBytes
92
+ * @param {Uint8Array} publicKeyBytes
93
+ */
94
+ constructor(privateKeyBytes, publicKeyBytes) {
95
+ Object.defineProperty(this, "privateKeyBytes", {
96
+ enumerable: false,
97
+ configurable: false,
98
+ writable: false,
99
+ value: new Uint8Array(privateKeyBytes),
100
+ });
101
+ this.publicKeyBytes = new Uint8Array(publicKeyBytes);
102
+ }
103
+
104
+ toJSON() {
105
+ return {};
106
+ }
107
+ }
108
+
109
+ /**
110
+ * AbstractSigner base (minimal).
111
+ */
112
+ class AbstractSigner {
113
+ /**
114
+ * @param {import("../providers/provider").AbstractProvider|null} provider
115
+ */
116
+ constructor(provider) {
117
+ this.provider = provider || null;
118
+ }
119
+
120
+ async getAddress() {
121
+ throw makeError("getAddress not implemented", "NOT_IMPLEMENTED", {});
122
+ }
123
+ }
124
+
125
+ /**
126
+ * BaseWallet - core signing implementation.
127
+ */
128
+ class BaseWallet extends AbstractSigner {
129
+ /**
130
+ * @param {SigningKey} signingKey
131
+ * @param {import("../providers/provider").AbstractProvider|null=} provider
132
+ * @param {{ address: string }=} precomputed
133
+ * @param {any=} qcWallet Internal quantum-coin-js-sdk Wallet (optional)
134
+ */
135
+ constructor(signingKey, provider, precomputed, qcWallet) {
136
+ super(provider || null);
137
+ _requireInitialized();
138
+
139
+ Object.defineProperty(this, "signingKey", {
140
+ enumerable: false,
141
+ configurable: false,
142
+ writable: false,
143
+ value: signingKey,
144
+ });
145
+ Object.defineProperty(this, "_qcWallet", {
146
+ enumerable: false,
147
+ configurable: true,
148
+ writable: true,
149
+ value: qcWallet || null,
150
+ });
151
+
152
+ /** @type {string} */
153
+ this.address = precomputed?.address || "";
154
+
155
+ Object.defineProperty(this, "privateKey", {
156
+ enumerable: false,
157
+ get: () => bytesToHex(this.signingKey.privateKeyBytes),
158
+ });
159
+
160
+ Object.defineProperty(this, "publicKey", {
161
+ enumerable: true,
162
+ get: () => bytesToHex(this.signingKey.publicKeyBytes),
163
+ });
164
+
165
+ Object.defineProperty(this, "_seed", {
166
+ enumerable: false,
167
+ configurable: true,
168
+ writable: true,
169
+ value: null,
170
+ });
171
+
172
+ Object.defineProperty(this, "seed", {
173
+ enumerable: false,
174
+ get: () => this._seed,
175
+ });
176
+ }
177
+
178
+ toJSON() {
179
+ return { address: this.address };
180
+ }
181
+
182
+ async getAddress() {
183
+ return this.address;
184
+ }
185
+
186
+ /**
187
+ * Sign a transaction using quantum-coin-js-sdk signRawTransaction().
188
+ * @param {import("../providers/provider").TransactionRequest} tx
189
+ * @returns {Promise<string>}
190
+ */
191
+ async signTransaction(tx) {
192
+ const { raw } = await this._signDetailed(tx);
193
+ return raw;
194
+ }
195
+
196
+ /**
197
+ * Internal: sign a transaction and return both the raw serialized transaction
198
+ * and the signer-computed transaction hash. The hash is later used to
199
+ * verify that an untrusted RPC node broadcast exactly the transaction we
200
+ * signed rather than substituting a different one.
201
+ * @param {import("../providers/provider").TransactionRequest} tx
202
+ * @returns {Promise<{ raw: string, hash: string|null }>}
203
+ */
204
+ async _signDetailed(tx) {
205
+ _requireInitialized();
206
+ assertArgument(tx && typeof tx === "object", "invalid tx", "tx", tx);
207
+
208
+ const toAddress = tx.to == null ? null : getAddress(tx.to);
209
+ const valueInWei = tx.value == null ? 0n : _getBigInt(tx.value, "tx.value");
210
+ const gasLimit = tx.gasLimit == null ? 21000 : _getNumber(tx.gasLimit, "tx.gasLimit");
211
+
212
+ const data = tx.data == null ? null : normalizeHex(tx.data);
213
+ const remarks = tx.remarks == null ? null : normalizeHex(tx.remarks);
214
+
215
+ if (remarks != null) {
216
+ assertArgument(isHexString(remarks), "remarks must be hex string", "remarks", remarks);
217
+ const remarkBytes = hexToBytes(remarks);
218
+ assertArgument(remarkBytes.length <= 32, "remarks too long (max 32 bytes)", "remarks", remarks);
219
+ }
220
+
221
+ // Nonce must be provided or resolved from provider.
222
+ let nonce = tx.nonce;
223
+ if (nonce == null) {
224
+ if (!this.provider) throw makeError("missing provider to resolve nonce", "UNKNOWN_ERROR", { operation: "signTransaction" });
225
+ // Prefer pending to avoid nonce collisions with in-flight transactions.
226
+ try {
227
+ nonce = await this.provider.getTransactionCount(this.address, "pending");
228
+ } catch {
229
+ nonce = await this.provider.getTransactionCount(this.address, "latest");
230
+ }
231
+ }
232
+ assertArgument(Number.isInteger(nonce) && nonce >= 0, "invalid nonce", "nonce", nonce);
233
+
234
+ // `chainId` remains accepted from the tx (e.g. via contract overrides). We
235
+ // intentionally do NOT assert it against the
236
+ // provider here because `chainId` in quantum-coin-js-sdk (qcsdk) defaults to
237
+ // 123123 internally — an unset chainId resolves to that network default rather
238
+ // than being left unbound. Cross-chain replay risk is therefore bounded by the
239
+ // qcsdk default; explicit per-call chainId overrides are caller responsibility.
240
+ const chainId = tx.chainId != null ? tx.chainId : (this.provider && this.provider.chainId != null ? this.provider.chainId : null);
241
+ const signingContext = tx.signingContext ?? null;
242
+
243
+ // chainId is mandatory for signing. Without it the signed transaction is
244
+ // not bound to a network and can be replayed on a different chain. Online
245
+ // flows resolve chainId from the connected provider; offline signing must
246
+ // pass tx.chainId explicitly.
247
+ let chainIdNum = null;
248
+ try {
249
+ chainIdNum = chainId == null ? null : _getNumber(chainId, "tx.chainId");
250
+ } catch {
251
+ chainIdNum = null;
252
+ }
253
+ assertArgument(
254
+ chainIdNum != null && chainIdNum >= 0,
255
+ "chainId is required for signing; pass tx.chainId or connect to a provider",
256
+ "chainId",
257
+ chainId,
258
+ );
259
+
260
+ /** @type {any} */
261
+ const qcWallet =
262
+ this._qcWallet ||
263
+ new qcsdk.Wallet(
264
+ this.address,
265
+ _bytesToNumberArray(this.signingKey.privateKeyBytes),
266
+ _bytesToNumberArray(this.signingKey.publicKeyBytes),
267
+ );
268
+
269
+ const req = new qcsdk.TransactionSigningRequest(
270
+ qcWallet,
271
+ toAddress,
272
+ valueInWei,
273
+ nonce,
274
+ data,
275
+ gasLimit,
276
+ remarks,
277
+ chainId,
278
+ signingContext,
279
+ );
280
+ const signResult = qcsdk.signRawTransaction(req);
281
+ // quantum-coin-js-sdk returns a SignResult object: { resultCode, txnHash, txnData }
282
+ if (!signResult || typeof signResult !== "object") {
283
+ throw makeError("signRawTransaction failed", "UNKNOWN_ERROR", {});
284
+ }
285
+ if (typeof signResult.resultCode === "number" && signResult.resultCode !== 0) {
286
+ throw makeError("signRawTransaction failed", "UNKNOWN_ERROR", {
287
+ resultCode: signResult.resultCode,
288
+ hash: signResult.txnHash || null,
289
+ });
290
+ }
291
+ const raw = signResult.txnData;
292
+ if (typeof raw !== "string") throw makeError("signRawTransaction did not return txnData", "UNKNOWN_ERROR", {});
293
+ let hash = null;
294
+ if (typeof signResult.txnHash === "string" && signResult.txnHash.length > 0) {
295
+ hash = normalizeHex(signResult.txnHash).toLowerCase();
296
+ }
297
+ return { raw: normalizeHex(raw), hash };
298
+ }
299
+
300
+ /**
301
+ * Signs and sends a transaction.
302
+ * @param {import("../providers/provider").TransactionRequest} tx
303
+ * @returns {Promise<import("../providers/provider").TransactionResponse>}
304
+ */
305
+ async sendTransaction(tx) {
306
+ if (!this.provider) throw makeError("missing provider", "UNKNOWN_ERROR", { operation: "sendTransaction" });
307
+ const { raw, hash } = await this._signDetailed({ ...tx, from: this.address });
308
+ // Tell the provider which hash we expect so it can reject a node that
309
+ // broadcasts (or echoes back) a different transaction than the one we signed.
310
+ return this.provider.sendTransaction(raw, hash ? { expectedHash: hash } : undefined);
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Wallet - convenience methods around BaseWallet.
316
+ */
317
+ class Wallet extends BaseWallet {
318
+ /**
319
+ * @param {string|Uint8Array|SigningKey} key
320
+ * @param {import("../providers/provider").AbstractProvider=} provider
321
+ */
322
+ constructor(key, provider) {
323
+ _requireInitialized();
324
+
325
+ let signingKey;
326
+ let qcAddress;
327
+
328
+ if (key instanceof SigningKey) {
329
+ signingKey = key;
330
+ // Compute address from public key
331
+ const addr = qcsdk.addressFromPublicKey(_bytesToNumberArray(signingKey.publicKeyBytes));
332
+ if (typeof addr !== "string") throw makeError("addressFromPublicKey failed", "UNKNOWN_ERROR", {});
333
+ qcAddress = normalizeHex(addr);
334
+ } else {
335
+ const privBytes = typeof key === "string" ? hexToBytes(key) : arrayify(key);
336
+ const pubHex = qcsdk.publicKeyFromPrivateKey(_bytesToNumberArray(privBytes));
337
+ if (typeof pubHex !== "string") throw makeError("publicKeyFromPrivateKey failed", "UNKNOWN_ERROR", {});
338
+ const pubBytes = hexToBytes(pubHex);
339
+ const addr = qcsdk.addressFromPublicKey(_bytesToNumberArray(pubBytes));
340
+ if (typeof addr !== "string") throw makeError("addressFromPublicKey failed", "UNKNOWN_ERROR", {});
341
+ qcAddress = normalizeHex(addr);
342
+ signingKey = new SigningKey(privBytes, pubBytes);
343
+ }
344
+
345
+ /** @type {any} */
346
+ const qcWallet = new qcsdk.Wallet(
347
+ qcAddress,
348
+ _bytesToNumberArray(signingKey.privateKeyBytes),
349
+ _bytesToNumberArray(signingKey.publicKeyBytes),
350
+ );
351
+
352
+ // Central choke point every Wallet (createRandom/fromPhrase/fromSeed/
353
+ // fromEncryptedJsonSync/fromKeys/connect all funnel through this constructor)
354
+ // is verified for key-pair consistency before it can be used to sign.
355
+ _assertKeyPairValid(signingKey.privateKeyBytes, signingKey.publicKeyBytes);
356
+
357
+ super(signingKey, provider || null, { address: qcAddress }, qcWallet);
358
+ }
359
+
360
+ /**
361
+ * Returns wallet address (sync).
362
+ * @returns {string}
363
+ */
364
+ getAddress() {
365
+ return this.address;
366
+ }
367
+
368
+ /**
369
+ * Returns wallet balance.
370
+ * @param {string=} blockTag
371
+ * @returns {Promise<bigint>}
372
+ */
373
+ async getBalance(blockTag) {
374
+ if (!this.provider) throw makeError("missing provider", "UNKNOWN_ERROR", { operation: "getBalance" });
375
+ void blockTag;
376
+ return this.provider.getBalance(this.address);
377
+ }
378
+
379
+ /**
380
+ * Returns wallet nonce.
381
+ * @param {string=} blockTag
382
+ * @returns {Promise<number>}
383
+ */
384
+ async getTransactionCount(blockTag) {
385
+ if (!this.provider) throw makeError("missing provider", "UNKNOWN_ERROR", { operation: "getTransactionCount" });
386
+ return this.provider.getTransactionCount(this.address, blockTag);
387
+ }
388
+
389
+ /**
390
+ * Encrypts and serializes this wallet to JSON.
391
+ * @param {string|Uint8Array} password
392
+ * @returns {string}
393
+ */
394
+ encryptSync(password) {
395
+ _requireInitialized();
396
+ if (this._seed != null) {
397
+ return Wallet.encryptSeedSync(hexToBytes(this._seed), password);
398
+ }
399
+ // A Uint8Array password is decoded as UTF-8 with NFC normalization below.
400
+ // For arbitrary (non-UTF-8) byte passwords this is
401
+ // lossy invalid sequences become U+FFFD — so a binary password may not
402
+ // round-trip and the keystore could fail to reopen. This is intentionally left
403
+ // as-is because the password->key mapping is part of the shared QuantumCoin
404
+ // keystore format owned by qcsdk (and interoperable with the Desktop/Mobile/Web
405
+ // /CLI wallets); changing it risks breaking existing keystores and cross-app
406
+ // interop. Prefer string passphrases. (Fixing correctly requires a keystore-spec
407
+ // decision, not a local change.)
408
+ const pw = typeof password === "string"
409
+ ? password.normalize("NFC")
410
+ : Buffer.from(arrayify(password)).toString("utf8").normalize("NFC");
411
+ assertSecretArgument(pw.length >= 12, "password must be at least 12 characters", "password");
412
+ const json = qcsdk.serializeEncryptedWallet(this._qcWallet, pw);
413
+ if (typeof json !== "string") throw makeError("serializeEncryptedWallet failed", "UNKNOWN_ERROR", {});
414
+ return json;
415
+ }
416
+
417
+ /**
418
+ * Encrypts raw seed bytes into a wallet JSON string (version 5 pre-expansion format).
419
+ * The resulting JSON can be opened with `Wallet.fromEncryptedJsonSync()` or
420
+ * Desktop/Mobile/Web/CLI wallet applications.
421
+ * @param {number[]|Uint8Array} seed Raw seed bytes: 64 (keyType 3), 72 (keyType 5), or 96 (legacy)
422
+ * @param {string|Uint8Array} password Passphrase (at least 12 characters)
423
+ * @returns {string}
424
+ */
425
+ static encryptSeedSync(seed, password) {
426
+ _requireInitialized();
427
+ const seedArr = seed instanceof Uint8Array ? Array.from(seed) : seed;
428
+ assertArgument(Array.isArray(seedArr), "seed must be an array of numbers or Uint8Array", "seed", seed);
429
+ const allowedLengths = [64, 72, 96];
430
+ assertArgument(allowedLengths.includes(seedArr.length), "seed must be 64, 72, or 96 bytes", "seed", seedArr.length);
431
+ // See encryptSync — a Uint8Array password is decoded as UTF-8/NFC, which is
432
+ // lossy for arbitrary byte passwords. Left as-is
433
+ // because the password->key mapping is part of the shared qcsdk keystore format
434
+ // (cross-app interop); prefer string passphrases.
435
+ const pw = typeof password === "string"
436
+ ? password.normalize("NFC")
437
+ : Buffer.from(arrayify(password)).toString("utf8").normalize("NFC");
438
+ assertSecretArgument(pw.length >= 12, "password must be at least 12 characters", "password");
439
+ const json = qcsdk.serializeSeedAsEncryptedWallet(seedArr, pw);
440
+ if (typeof json !== "string") throw makeError("serializeSeedAsEncryptedWallet failed", "UNKNOWN_ERROR", {});
441
+ return json;
442
+ }
443
+
444
+ /**
445
+ * Returns the seed phrase (list of words) if this wallet has a seed, else null.
446
+ * Derived from the stored pre-expansion seed via seed-words.getWordListFromSeedArray.
447
+ *
448
+ * Non-null for wallets created via createRandom, fromPhrase, fromSeed, and
449
+ * fromEncryptedJsonSync when the JSON is a version-5 keystore produced by
450
+ * encryptSync on a seed-bearing wallet or by encryptSeedSync.
451
+ * Null for fromKeys and for v3/v4 keystores without preExpansionSeed.
452
+ *
453
+ * @returns {string[]|null}
454
+ */
455
+ getPhrase() {
456
+ _requireInitialized();
457
+ if (this._seed == null) return null;
458
+ const bytes = Array.from(hexToBytes(this._seed));
459
+ const words = seedWords.getWordListFromSeedArray(bytes);
460
+ return Array.isArray(words) ? words : null;
461
+ }
462
+
463
+ /**
464
+ * Returns the recommended signing context for this wallet.
465
+ * Setting fullSign to true may incur additional gas cost.
466
+ * @param {boolean|null=} fullSign Defaults to false when null or omitted.
467
+ * @returns {number}
468
+ */
469
+ getSigningContext(fullSign) {
470
+ const fs = fullSign ?? false;
471
+ const pubLen = this.signingKey.publicKeyBytes.length;
472
+ if (pubLen === 1408) {
473
+ return fs ? 2 : 0;
474
+ }
475
+ if (pubLen === 2688) {
476
+ return 1;
477
+ }
478
+ throw makeError("unsupported public key size", "UNSUPPORTED_OPERATION", { publicKeyLength: pubLen });
479
+ }
480
+
481
+ /**
482
+ * Returns a new wallet connected to a provider.
483
+ * @param {import("../providers/provider").AbstractProvider} provider
484
+ * @returns {Wallet}
485
+ */
486
+ connect(provider) {
487
+ const w = new Wallet(this.signingKey, provider);
488
+ w._qcWallet = this._qcWallet;
489
+ w._seed = this._seed;
490
+ return w;
491
+ }
492
+
493
+ /**
494
+ * Creates a new random wallet.
495
+ * @param {import("../providers/provider").AbstractProvider=} provider
496
+ * @param {number|null=} keyType Optional key type: null (default=3), 3, or 5
497
+ * @returns {Wallet}
498
+ */
499
+ static createRandom(provider, keyType) {
500
+ _requireInitialized();
501
+ if (keyType != null) {
502
+ assertArgument(keyType === 3 || keyType === 5, "keyType must be null, 3, or 5", "keyType", keyType);
503
+ }
504
+ const words = qcsdk.newWalletSeedWords(keyType ?? null);
505
+ if (!words || !Array.isArray(words)) {
506
+ throw makeError("newWalletSeedWords failed", "UNKNOWN_ERROR", { result: words });
507
+ }
508
+ return Wallet.fromPhrase(words, provider);
509
+ }
510
+
511
+ /**
512
+ * Creates a wallet from raw seed bytes.
513
+ * @param {number[]} seed Raw seed bytes: 64 (keyType 3), 72 (keyType 5), or 96 (legacy)
514
+ * @param {import("../providers/provider").AbstractProvider=} provider
515
+ * @returns {Wallet}
516
+ */
517
+ static fromSeed(seed, provider) {
518
+ _requireInitialized();
519
+ assertArgument(Array.isArray(seed), "seed must be an array of numbers", "seed", seed);
520
+ const allowedLengths = [64, 72, 96];
521
+ assertArgument(allowedLengths.includes(seed.length), "seed must be 64, 72, or 96 bytes", "seed", seed.length);
522
+ const qcWallet = qcsdk.openWalletFromSeed(seed);
523
+ if (!qcWallet || typeof qcWallet === "number") {
524
+ throw makeError("openWalletFromSeed failed", "UNKNOWN_ERROR", { result: qcWallet });
525
+ }
526
+ return Wallet._fromQcWallet(qcWallet, provider || null);
527
+ }
528
+
529
+ /**
530
+ * Creates a wallet from an encrypted JSON string.
531
+ * @param {string} json
532
+ * @param {string} password
533
+ * @param {import("../providers/provider").AbstractProvider=} provider
534
+ * @returns {Wallet}
535
+ */
536
+ static fromEncryptedJsonSync(json, password, provider) {
537
+ _requireInitialized();
538
+ const qcWallet = qcsdk.deserializeEncryptedWallet(json, password);
539
+ if (!qcWallet) throw makeError("deserializeEncryptedWallet failed", "UNKNOWN_ERROR", {});
540
+ return Wallet._fromQcWallet(qcWallet, provider || null);
541
+ }
542
+
543
+ /**
544
+ * Creates a wallet from a seed phrase (32, 36, or 48 words).
545
+ * @param {string|string[]} phrase
546
+ * @param {import("../providers/provider").AbstractProvider=} provider
547
+ * @returns {Wallet}
548
+ */
549
+ static fromPhrase(phrase, provider) {
550
+ _requireInitialized();
551
+ let words = phrase;
552
+ if (typeof phrase === "string") {
553
+ words = phrase
554
+ .split(/[,\s]+/g)
555
+ .map((w) => w.trim())
556
+ .filter(Boolean);
557
+ }
558
+ assertArgument(Array.isArray(words), "phrase must be a string or string[]", "phrase", phrase);
559
+ const allowedLengths = [32, 36, 48];
560
+ assertArgument(
561
+ allowedLengths.includes(words.length),
562
+ "seed phrase must contain 32, 36, or 48 words",
563
+ "phrase",
564
+ words.length,
565
+ );
566
+ const qcWallet = qcsdk.openWalletFromSeedWords(words);
567
+ if (!qcWallet) throw makeError("openWalletFromSeedWords failed", "UNKNOWN_ERROR", {});
568
+ return Wallet._fromQcWallet(qcWallet, provider || null);
569
+ }
570
+
571
+ /**
572
+ * Creates a wallet from raw private and public key bytes.
573
+ * @param {Uint8Array|string} privateKey Private key bytes or hex string
574
+ * @param {Uint8Array|string} publicKey Public key bytes or hex string
575
+ * @param {import("../providers/provider").AbstractProvider=} provider
576
+ * @returns {Wallet}
577
+ */
578
+ static fromKeys(privateKey, publicKey, provider) {
579
+ _requireInitialized();
580
+ const privBytes = typeof privateKey === "string" ? hexToBytes(privateKey) : arrayify(privateKey);
581
+ const pubBytes = typeof publicKey === "string" ? hexToBytes(publicKey) : arrayify(publicKey);
582
+ assertSecretArgument(privBytes.length > 0, "privateKey must not be empty", "privateKey");
583
+ assertSecretArgument(pubBytes.length > 0, "publicKey must not be empty", "publicKey");
584
+
585
+ const privArr = _bytesToNumberArray(privBytes);
586
+ const pubArr = _bytesToNumberArray(pubBytes);
587
+ const addr = qcsdk.addressFromPublicKey(pubArr);
588
+ if (typeof addr !== "string") throw makeError("addressFromPublicKey failed", "UNKNOWN_ERROR", {});
589
+
590
+ const qcWallet = new qcsdk.Wallet(addr, privArr, pubArr);
591
+ // Key-pair verification now happens centrally in the Wallet constructor
592
+ // (reached via _fromQcWallet -> new Wallet). No redundant verify here.
593
+ return Wallet._fromQcWallet(qcWallet, provider || null);
594
+ }
595
+
596
+ /**
597
+ * Internal helper: build a Wallet from a quantum-coin-js-sdk Wallet object.
598
+ * @param {any} qcWallet
599
+ * @param {import("../providers/provider").AbstractProvider|null} provider
600
+ * @returns {Wallet}
601
+ */
602
+ static _fromQcWallet(qcWallet, provider) {
603
+ const privSrc = qcWallet.privateKey;
604
+ const pubSrc = qcWallet.publicKey;
605
+
606
+ if (!privSrc || (privSrc instanceof Uint8Array && privSrc.length === 0) || (Array.isArray(privSrc) && privSrc.length === 0)) {
607
+ throw makeError("qcWallet.privateKey is empty or missing", "UNKNOWN_ERROR", {});
608
+ }
609
+ if (!pubSrc || (pubSrc instanceof Uint8Array && pubSrc.length === 0) || (Array.isArray(pubSrc) && pubSrc.length === 0)) {
610
+ throw makeError("qcWallet.publicKey is empty or missing", "UNKNOWN_ERROR", {});
611
+ }
612
+
613
+ const privBytes =
614
+ privSrc instanceof Uint8Array ? new Uint8Array(privSrc) : Uint8Array.from(Array.from(privSrc).map((n) => (Number(n) & 0xff)));
615
+ const pubBytes =
616
+ pubSrc instanceof Uint8Array ? new Uint8Array(pubSrc) : Uint8Array.from(Array.from(pubSrc).map((n) => (Number(n) & 0xff)));
617
+ const key = new SigningKey(privBytes, pubBytes);
618
+
619
+ const w = new Wallet(key, provider || null);
620
+ // Ensure we keep the exact underlying qcWallet for operations like encrypt/signRawTransaction.
621
+ w._qcWallet = qcWallet;
622
+ if (typeof qcWallet.address === "string") {
623
+ w.address = normalizeHex(qcWallet.address);
624
+ }
625
+ if (qcWallet.preExpansionSeed != null) {
626
+ const seedSrc = qcWallet.preExpansionSeed;
627
+ const seedBytes =
628
+ seedSrc instanceof Uint8Array ? seedSrc : Uint8Array.from(Array.from(seedSrc).map((n) => (Number(n) & 0xff)));
629
+ w._seed = bytesToHex(seedBytes);
630
+ }
631
+ return w;
632
+ }
633
+ }
634
+
635
+ /**
636
+ * NonceManager wrapper.
637
+ */
638
+ class NonceManager extends AbstractSigner {
639
+ /**
640
+ * @param {AbstractSigner} signer
641
+ */
642
+ constructor(signer) {
643
+ super(signer.provider || null);
644
+ this.signer = signer;
645
+ this._nonce = null;
646
+ }
647
+
648
+ async getAddress() {
649
+ return this.signer.getAddress();
650
+ }
651
+
652
+ async getTransactionCount(blockTag) {
653
+ if (this._nonce != null) return this._nonce;
654
+ if (!this.provider) throw makeError("missing provider", "UNKNOWN_ERROR", { operation: "getTransactionCount" });
655
+ const address = await this.getAddress();
656
+ this._nonce = await this.provider.getTransactionCount(address, blockTag);
657
+ return this._nonce;
658
+ }
659
+
660
+ async sendTransaction(tx) {
661
+ const nonce = await this.getTransactionCount("latest");
662
+ const result = await this.signer.sendTransaction({ ...tx, nonce });
663
+ this._nonce = nonce + 1;
664
+ return result;
665
+ }
666
+
667
+ reset() {
668
+ this._nonce = null;
669
+ }
670
+
671
+ increment() {
672
+ if (this._nonce == null) this._nonce = 0;
673
+ this._nonce++;
674
+ }
675
+ }
676
+
677
+ /**
678
+ * JsonRpcSigner placeholder (ethers-like).
679
+ * This SDK encourages using Wallet directly for signing.
680
+ */
681
+ class JsonRpcSigner extends AbstractSigner {
682
+ constructor(provider, address) {
683
+ super(provider);
684
+ this._address = address;
685
+ }
686
+ async getAddress() {
687
+ return this._address;
688
+ }
689
+ }
690
+
691
+ /**
692
+ * VoidSigner (cannot sign, only provides address).
693
+ */
694
+ class VoidSigner extends AbstractSigner {
695
+ constructor(address, provider) {
696
+ super(provider || null);
697
+ this._address = getAddress(address);
698
+ }
699
+ async getAddress() {
700
+ return this._address;
701
+ }
702
+ }
703
+
704
+ module.exports = {
705
+ SigningKey,
706
+ AbstractSigner,
707
+ BaseWallet,
708
+ Wallet,
709
+ NonceManager,
710
+ JsonRpcSigner,
711
+ VoidSigner,
712
+ };
713
+