quantumcoin 7.0.10 → 7.0.12
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.
- package/README-SDK.md +16 -5
- package/README.md +14 -3
- package/config.js +10 -2
- package/examples/example.js +0 -5
- package/examples/example.ts +0 -5
- package/examples/node_modules/.bin/esbuild +16 -0
- package/examples/node_modules/.bin/esbuild.cmd +17 -0
- package/examples/node_modules/.bin/esbuild.ps1 +28 -0
- package/examples/node_modules/.bin/sdkgen +16 -0
- package/examples/node_modules/.bin/sdkgen.cmd +17 -0
- package/examples/node_modules/.bin/sdkgen.ps1 +28 -0
- package/examples/node_modules/.bin/tsx +16 -0
- package/examples/node_modules/.bin/tsx.cmd +17 -0
- package/examples/node_modules/.bin/tsx.ps1 +28 -0
- package/examples/node_modules/.package-lock.json +144 -0
- package/examples/node_modules/@esbuild/win32-x64/README.md +3 -0
- package/examples/node_modules/@esbuild/win32-x64/esbuild.exe +0 -0
- package/examples/node_modules/@esbuild/win32-x64/package.json +20 -0
- package/examples/node_modules/esbuild/LICENSE.md +21 -0
- package/examples/node_modules/esbuild/README.md +3 -0
- package/examples/node_modules/esbuild/bin/esbuild +223 -0
- package/examples/node_modules/esbuild/install.js +289 -0
- package/examples/node_modules/esbuild/lib/main.d.ts +716 -0
- package/examples/node_modules/esbuild/lib/main.js +2532 -0
- package/examples/node_modules/esbuild/package.json +49 -0
- package/examples/node_modules/get-tsconfig/LICENSE +21 -0
- package/examples/node_modules/get-tsconfig/README.md +235 -0
- package/examples/node_modules/get-tsconfig/dist/index.cjs +7 -0
- package/examples/node_modules/get-tsconfig/dist/index.d.cts +2088 -0
- package/examples/node_modules/get-tsconfig/dist/index.d.mts +2088 -0
- package/examples/node_modules/get-tsconfig/dist/index.mjs +7 -0
- package/examples/node_modules/get-tsconfig/package.json +46 -0
- package/examples/node_modules/quantum-coin-js-sdk/LICENSE +21 -0
- package/examples/node_modules/quantum-coin-js-sdk/LICENSE-wasm_exec.js.txt +30 -0
- package/examples/node_modules/quantum-coin-js-sdk/README.md +1675 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/README.md +14 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/conversion-example.js +19 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-create-contract.js +396 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-encode-decode-rlp.js +225 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-event-pack-unpack.js +391 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-misc.js +101 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-rpc-send-signRawTransaction.js +318 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-rpc-send.js +116 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-send.js +70 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-token-pack-unpack.js +961 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-wallet-version4.js +35 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-wallet.js +43 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example.js +405 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/package-lock.json +134 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/package.json +15 -0
- package/examples/node_modules/quantum-coin-js-sdk/index.d.ts +1031 -0
- package/examples/node_modules/quantum-coin-js-sdk/index.js +3144 -0
- package/examples/node_modules/quantum-coin-js-sdk/package.json +34 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/encrypted-32.json +1 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/encrypted-36.json +1 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/encrypted-48.json +1 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/generate-verify-vectors.js +91 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/non-transactional.preinit.test.js +41 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/non-transactional.test.js +1389 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/sign-raw-keytype5-context-null.test.js +107 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/sign-raw-transaction.test.js +196 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/sign-verify.test.js +311 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/transactional.relay.test.js +131 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/transactional.rpc.test.js +103 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/verify-vectors.json +95035 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasmBase64.d.ts +9 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasmBase64.js +16 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasm_exec.d.ts +0 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasm_exec.js +587 -0
- package/examples/node_modules/resolve-pkg-maps/LICENSE +21 -0
- package/examples/node_modules/resolve-pkg-maps/README.md +216 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.cjs +1 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.d.cts +11 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.d.mts +11 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.mjs +1 -0
- package/examples/node_modules/resolve-pkg-maps/package.json +42 -0
- package/examples/node_modules/seed-words/.github/workflows/publish-npmjs.yaml +22 -0
- package/examples/node_modules/seed-words/BUILD.md +7 -0
- package/examples/node_modules/seed-words/LICENSE +121 -0
- package/examples/node_modules/seed-words/README.md +67 -0
- package/examples/node_modules/seed-words/dist/seedwords.d.ts +39 -0
- package/examples/node_modules/seed-words/package.json +27 -0
- package/examples/node_modules/seed-words/seedwords.js +315 -0
- package/examples/node_modules/seed-words/seedwords.txt +65536 -0
- package/examples/node_modules/seed-words/tsconfig.json +21 -0
- package/examples/node_modules/tsx/LICENSE +21 -0
- package/examples/node_modules/tsx/README.md +32 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.cjs +1 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.d.cts +35 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.d.mts +35 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.mjs +1 -0
- package/examples/node_modules/tsx/dist/cjs/index.cjs +1 -0
- package/examples/node_modules/tsx/dist/cjs/index.mjs +1 -0
- package/examples/node_modules/tsx/dist/cli.cjs +54 -0
- package/examples/node_modules/tsx/dist/cli.mjs +55 -0
- package/examples/node_modules/tsx/dist/client-BQVF1NaW.mjs +1 -0
- package/examples/node_modules/tsx/dist/client-D6NvIMSC.cjs +1 -0
- package/examples/node_modules/tsx/dist/esm/api/index.cjs +1 -0
- package/examples/node_modules/tsx/dist/esm/api/index.d.cts +35 -0
- package/examples/node_modules/tsx/dist/esm/api/index.d.mts +35 -0
- package/examples/node_modules/tsx/dist/esm/api/index.mjs +1 -0
- package/examples/node_modules/tsx/dist/esm/index.cjs +2 -0
- package/examples/node_modules/tsx/dist/esm/index.mjs +2 -0
- package/examples/node_modules/tsx/dist/get-pipe-path-BHW2eJdv.mjs +1 -0
- package/examples/node_modules/tsx/dist/get-pipe-path-BoR10qr8.cjs +1 -0
- package/examples/node_modules/tsx/dist/index-7AaEi15b.mjs +14 -0
- package/examples/node_modules/tsx/dist/index-BWFBUo6r.cjs +1 -0
- package/examples/node_modules/tsx/dist/index-gbaejti9.mjs +1 -0
- package/examples/node_modules/tsx/dist/index-gckBtVBf.cjs +14 -0
- package/examples/node_modules/tsx/dist/lexer-DQCqS3nf.mjs +3 -0
- package/examples/node_modules/tsx/dist/lexer-DgIbo0BU.cjs +3 -0
- package/examples/node_modules/tsx/dist/loader.cjs +1 -0
- package/examples/node_modules/tsx/dist/loader.mjs +1 -0
- package/examples/node_modules/tsx/dist/node-features-_8ZFwP_x.mjs +1 -0
- package/examples/node_modules/tsx/dist/node-features-roYmp9jK.cjs +1 -0
- package/examples/node_modules/tsx/dist/package-CeBgXWuR.mjs +1 -0
- package/examples/node_modules/tsx/dist/package-Dxt5kIHw.cjs +1 -0
- package/examples/node_modules/tsx/dist/patch-repl.cjs +1 -0
- package/examples/node_modules/tsx/dist/patch-repl.mjs +1 -0
- package/examples/node_modules/tsx/dist/preflight.cjs +1 -0
- package/examples/node_modules/tsx/dist/preflight.mjs +1 -0
- package/examples/node_modules/tsx/dist/register-2sWVXuRQ.cjs +1 -0
- package/examples/node_modules/tsx/dist/register-B7jrtLTO.mjs +1 -0
- package/examples/node_modules/tsx/dist/register-CFH5oNdT.mjs +4 -0
- package/examples/node_modules/tsx/dist/register-D46fvsV_.cjs +4 -0
- package/examples/node_modules/tsx/dist/repl.cjs +3 -0
- package/examples/node_modules/tsx/dist/repl.mjs +3 -0
- package/examples/node_modules/tsx/dist/require-D4F1Lv60.cjs +1 -0
- package/examples/node_modules/tsx/dist/require-DQxpCAr4.mjs +1 -0
- package/examples/node_modules/tsx/dist/suppress-warnings.cjs +1 -0
- package/examples/node_modules/tsx/dist/suppress-warnings.mjs +1 -0
- package/examples/node_modules/tsx/dist/temporary-directory-B83uKxJF.cjs +1 -0
- package/examples/node_modules/tsx/dist/temporary-directory-CwHp0_NW.mjs +1 -0
- package/examples/node_modules/tsx/dist/types-Cxp8y2TL.d.ts +5 -0
- package/examples/node_modules/tsx/package.json +68 -0
- package/examples/offline-signing.js +0 -2
- package/examples/offline-signing.ts +0 -1
- package/examples/package-lock.json +422 -73
- package/examples/package.json +1 -1
- package/examples/wallet-offline.js +1 -9
- package/examples/wallet-offline.ts +1 -9
- package/generate-sdk.js +5 -7
- package/package.json +2 -2
- package/src/abi/interface.js +13 -7
- package/src/abi/js-abi-coder.js +23 -18
- package/src/constants.d.ts +0 -5
- package/src/constants.js +0 -7
- package/src/contract/contract-factory.js +9 -3
- package/src/contract/contract.js +9 -3
- package/src/errors/index.js +12 -0
- package/src/index.d.ts +0 -3
- package/src/providers/extra-providers.js +20 -6
- package/src/providers/json-rpc-provider.js +15 -5
- package/src/providers/provider.d.ts +0 -2
- package/src/providers/provider.js +1 -3
- package/src/utils/address.d.ts +0 -14
- package/src/utils/address.js +12 -49
- package/src/utils/hashing.d.ts +0 -6
- package/src/utils/hashing.js +8 -23
- package/src/utils/index.d.ts +0 -3
- package/src/utils/rlp.js +7 -4
- package/src/wallet/wallet.d.ts +7 -13
- package/src/wallet/wallet.js +136 -97
- package/test/security/malformed-input.test.js +295 -1
- package/test/unit/address-wallet.test.js +329 -129
- package/test/unit/address-wallet.test.ts +328 -128
- package/test/unit/hashing.test.js +0 -11
- package/test/unit/hashing.test.ts +0 -11
- package/test/unit/providers.test.js +3 -1
- package/test/unit/providers.test.ts +3 -1
- package/SPEC.md +0 -3845
|
@@ -0,0 +1,1389 @@
|
|
|
1
|
+
const { describe, test, before } = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
// Tests are required to live under `tests/` per project convention.
|
|
7
|
+
// These are the "non-transactional" tests: offline operations + read-only relay/RPC calls.
|
|
8
|
+
|
|
9
|
+
const qcsdk = require('..');
|
|
10
|
+
|
|
11
|
+
const MAINNET_CHAIN_ID = 123123;
|
|
12
|
+
const READ_RELAY_URL = 'https://sdk.readrelay.quantumcoinapi.com';
|
|
13
|
+
const WRITE_RELAY_URL = 'https://sdk.writerelay.quantumcoinapi.com';
|
|
14
|
+
const PUBLIC_RPC_URL = 'https://public.rpc.quantumcoinapi.com';
|
|
15
|
+
|
|
16
|
+
const VALID_ADDRESS_EXAMPLE =
|
|
17
|
+
'0x6f605c4142f1cb037f967101a5b28ccd00b27cce4516190356baaf284d20e667';
|
|
18
|
+
const VALID_ADDRESS_EXAMPLE_UPPER =
|
|
19
|
+
'0X6F605C4142F1CB037F967101A5B28CCD00B27CCE4516190356BAAF284D20E667';
|
|
20
|
+
const TO_ADDRESS_EXAMPLE =
|
|
21
|
+
'0x8293cd9b6ac502d2fe077b0c157dad39f36a5e546525b053151dced633634612';
|
|
22
|
+
const ACCOUNT_ADDRESS_EXAMPLE =
|
|
23
|
+
'0x0000000000000000000000000000000000000000000000000000000000001000';
|
|
24
|
+
const ACCOUNT_TX_LIST_EXAMPLE =
|
|
25
|
+
'0x0000000000000000000000000000000000000000000000000000000000002000';
|
|
26
|
+
const TX_HASH_EXAMPLE =
|
|
27
|
+
'0xe6fbabc178adaaab6b9dbda086de53deaced1d6fe40e7db9539fe9e85695d1be';
|
|
28
|
+
|
|
29
|
+
// Example seed words (48) from `example/example.js`
|
|
30
|
+
const SEED_WORD_LIST =
|
|
31
|
+
'servetize,redmation,suaveton,dreadtolk,rondial,pondicle,miscoil,teaguery,dylodecid,portnel,mantical,slapware,sluthike,tactise,crierial,tajluvki,pranicum,sockcup,stacksong,duerling,genogram,peasate,pulubly,skimpop,feldtail,saprostal,crabrock,radiment,dolocsin,strigemen,juryeuk,fextial,merunized,tangienti,stylocyte,plumvieve,bobstike,nosecrown,acudemy,gripstick,lacreous,marculade,sporculum,outslope,bioburden,trompong,sidelay,finchage'
|
|
32
|
+
.split(',');
|
|
33
|
+
|
|
34
|
+
// Example wallet created with external wallet (from examples)
|
|
35
|
+
const EXAMPLE_WALLET_ENCRYPTED_JSON =
|
|
36
|
+
'{"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}';
|
|
37
|
+
const EXAMPLE_WALLET_PASSPHRASE = 'QuantumCoinExample123!';
|
|
38
|
+
|
|
39
|
+
// Fixture for "wallet test" (same as example wallet)
|
|
40
|
+
const TEST_WALLET_ENCRYPTED_JSON = EXAMPLE_WALLET_ENCRYPTED_JSON;
|
|
41
|
+
const TEST_WALLET_PASSPHRASE = EXAMPLE_WALLET_PASSPHRASE;
|
|
42
|
+
|
|
43
|
+
// Fixture for seed-words address test
|
|
44
|
+
const TEST_SEED_WORDS = [
|
|
45
|
+
'cylamidal', 'suculate', 'sealmate', 'radiploid', 'equifaxis', 'and', 'antipoise', 'stitchesy', 'perelade', 'lite',
|
|
46
|
+
'gourtarel', 'thursat', 'overdrome', 'cogulate', 'nonviva', 'stewnut', 'floribund', 'enduivist', 'decatary', 'elvenwort',
|
|
47
|
+
'indoucate', 'ravelent', 'vocalus', 'wetshirt', 'rutatory', 'percect', 'breaktout', 'corpation', 'myricorus', 'veofreat',
|
|
48
|
+
'junkard', 'supercarp', 'sukerus', 'tautang', 'facetype', 'shishkin', 'insulal', 'hobstone', 'stumbed', 'tecutonic',
|
|
49
|
+
'jumplike', 'hegwirth', 'idea', 'bhagatpur', 'pavastava', 'kukuluan', 'mageiline', 'extranite',
|
|
50
|
+
];
|
|
51
|
+
const TEST_SEED_ADDRESS = '0x3Ce22c0e2714196734E42B0D4D5AD11284260502A560e46c2Cd857391564142F'.toLowerCase();
|
|
52
|
+
|
|
53
|
+
// First 32 and 36 words from TEST_SEED_WORDS (for deterministic address tests)
|
|
54
|
+
const TEST_SEED_WORDS_32 = TEST_SEED_WORDS.slice(0, 32);
|
|
55
|
+
const TEST_SEED_WORDS_36 = TEST_SEED_WORDS.slice(0, 36);
|
|
56
|
+
// Expected addresses for first 32/36 words (deterministic from test run)
|
|
57
|
+
const TEST_SEED_ADDRESS_32 = '0x38b12df2d4762a04a183f936c47747a1f13d0b0ba72066b43b4b6d7f776e9e25';
|
|
58
|
+
const TEST_SEED_ADDRESS_36 = '0x030e264c853bd859c53fae3ad6ef0e011dc799685e2b05d5efa7ac50f10ca075';
|
|
59
|
+
|
|
60
|
+
// Default test password for encrypt/decrypt in seed-wallet roundtrip tests
|
|
61
|
+
const SEED_WALLET_TEST_PASSPHRASE = TEST_WALLET_PASSPHRASE;
|
|
62
|
+
|
|
63
|
+
async function rpc(method, params) {
|
|
64
|
+
const response = await fetch(PUBLIC_RPC_URL, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: { 'Content-Type': 'application/json' },
|
|
67
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
|
|
68
|
+
});
|
|
69
|
+
assert.ok(response.ok, `RPC HTTP ${response.status}`);
|
|
70
|
+
return response.json();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isHex0x(str) {
|
|
74
|
+
return typeof str === 'string' && /^0x[0-9a-fA-F]*$/.test(str);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isHex(str) {
|
|
78
|
+
return typeof str === 'string' && /^[0-9a-fA-F]+$/.test(str);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** True if CIRCL WASM is loaded, newWallet() returns a wallet, and verifyWallet passes. */
|
|
82
|
+
function isCirclAvailable() {
|
|
83
|
+
const w = qcsdk.newWallet();
|
|
84
|
+
return (
|
|
85
|
+
typeof w === 'object' &&
|
|
86
|
+
w != null &&
|
|
87
|
+
w.privateKey != null &&
|
|
88
|
+
w.address != null &&
|
|
89
|
+
qcsdk.verifyWallet(w) === true
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function hexToBytes(hex) {
|
|
94
|
+
assert.ok(typeof hex === 'string');
|
|
95
|
+
const h = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
96
|
+
assert.equal(h.length % 2, 0);
|
|
97
|
+
const out = [];
|
|
98
|
+
for (let i = 0; i < h.length; i += 2) out.push(parseInt(h.slice(i, i + 2), 16));
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
describe('non-transactional', () => {
|
|
103
|
+
test('exports: all classes are constructible (and not callable without new)', () => {
|
|
104
|
+
const classExports = [
|
|
105
|
+
'AccountDetails',
|
|
106
|
+
'AccountDetailsResult',
|
|
107
|
+
'AccountTransactionCompact',
|
|
108
|
+
'AccountTransactionsResult',
|
|
109
|
+
'BlockDetails',
|
|
110
|
+
'Config',
|
|
111
|
+
'EventLogEncodeResult',
|
|
112
|
+
'LatestBlockDetailsResult',
|
|
113
|
+
'ListAccountTransactionsResponse',
|
|
114
|
+
'PackUnpackResult',
|
|
115
|
+
'SendResult',
|
|
116
|
+
'TransactionDetails',
|
|
117
|
+
'TransactionDetailsResult',
|
|
118
|
+
'TransactionReceipt',
|
|
119
|
+
'TransactionSigningRequest',
|
|
120
|
+
'Wallet',
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
for (const name of classExports) {
|
|
124
|
+
assert.equal(typeof qcsdk[name], 'function', `${name} export missing`);
|
|
125
|
+
assert.throws(() => qcsdk[name](), TypeError, `${name} should require new`);
|
|
126
|
+
// Basic "positive" constructability check
|
|
127
|
+
assert.ok(new qcsdk[name](), `${name} should be new-able`);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
before(async () => {
|
|
132
|
+
const cfg = new qcsdk.Config(READ_RELAY_URL, WRITE_RELAY_URL, MAINNET_CHAIN_ID, '', '');
|
|
133
|
+
const initResult = await qcsdk.initialize(cfg);
|
|
134
|
+
assert.equal(initResult, true, 'SDK initialize should succeed');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('initialize: calling initialize twice returns false', async () => {
|
|
138
|
+
const cfg = new qcsdk.Config(READ_RELAY_URL, WRITE_RELAY_URL, MAINNET_CHAIN_ID, '', '');
|
|
139
|
+
const init2 = await qcsdk.initialize(cfg);
|
|
140
|
+
assert.equal(init2, false);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('isAddressValid: accepts valid 0x/0X and rejects invalid', () => {
|
|
144
|
+
assert.equal(qcsdk.isAddressValid(VALID_ADDRESS_EXAMPLE), true);
|
|
145
|
+
assert.equal(qcsdk.isAddressValid(VALID_ADDRESS_EXAMPLE_UPPER), true);
|
|
146
|
+
assert.equal(qcsdk.isAddressValid('asfasdfasdfs'), false);
|
|
147
|
+
assert.equal(qcsdk.isAddressValid(null), false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('wallet: newWallet/verifyWallet/serializeWallet/deserializeWallet roundtrip', () => {
|
|
151
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded and verifyWallet(newWallet()) must pass');
|
|
152
|
+
const w1 = qcsdk.newWallet();
|
|
153
|
+
assert.ok(w1);
|
|
154
|
+
assert.equal(typeof w1.address, 'string');
|
|
155
|
+
assert.ok(w1.privateKey && (w1.privateKey.byteLength !== undefined || typeof w1.privateKey.length === 'number'));
|
|
156
|
+
assert.ok(w1.publicKey && (w1.publicKey.byteLength !== undefined || typeof w1.publicKey.length === 'number'));
|
|
157
|
+
assert.ok(w1.privateKey.length > 0);
|
|
158
|
+
assert.ok(w1.publicKey.length > 0);
|
|
159
|
+
assert.equal(qcsdk.verifyWallet(w1), true);
|
|
160
|
+
assert.equal(qcsdk.isAddressValid(w1.address), true);
|
|
161
|
+
|
|
162
|
+
const serialized = qcsdk.serializeWallet(w1);
|
|
163
|
+
assert.ok(serialized && typeof serialized === 'string');
|
|
164
|
+
const w2 = qcsdk.deserializeWallet(serialized);
|
|
165
|
+
assert.ok(w2);
|
|
166
|
+
assert.equal(w2.address.toLowerCase(), w1.address.toLowerCase());
|
|
167
|
+
assert.equal(qcsdk.verifyWallet(w2), true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('wallet encrypted: serializeEncryptedWallet/deserializeEncryptedWallet', () => {
|
|
171
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded and verifyWallet(newWallet()) must pass');
|
|
172
|
+
const passphrase = 'QuantumCoinExample123!';
|
|
173
|
+
const w1 = qcsdk.newWallet();
|
|
174
|
+
const enc = qcsdk.serializeEncryptedWallet(w1, passphrase);
|
|
175
|
+
assert.ok(enc && typeof enc === 'string');
|
|
176
|
+
|
|
177
|
+
const w2 = qcsdk.deserializeEncryptedWallet(enc, passphrase);
|
|
178
|
+
assert.ok(w2);
|
|
179
|
+
assert.equal(w2.address.toLowerCase(), w1.address.toLowerCase());
|
|
180
|
+
assert.equal(qcsdk.verifyWallet(w2), true);
|
|
181
|
+
|
|
182
|
+
// Negative: wrong passphrase
|
|
183
|
+
const wBad = qcsdk.deserializeEncryptedWallet(enc, passphrase + 'wrong');
|
|
184
|
+
assert.equal(wBad, null);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('wallet test', () => {
|
|
188
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded and verifyWallet(newWallet()) must pass');
|
|
189
|
+
const wallet = qcsdk.deserializeEncryptedWallet(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE);
|
|
190
|
+
assert.ok(wallet, 'deserializeEncryptedWallet should return a wallet');
|
|
191
|
+
assert.equal(typeof wallet.address, 'string');
|
|
192
|
+
assert.equal(wallet.address.toLowerCase(), '0x1a846abe71c8b989e8337c55d608be81c28ab3b2e40c83eaa2a68d516049aec6');
|
|
193
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
194
|
+
assert.equal(qcsdk.isAddressValid(wallet.address), true);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('seed words: newWalletSeedWords/openWalletFromSeedWords (static fixture)', () => {
|
|
198
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded and verifyWallet(newWallet()) must pass');
|
|
199
|
+
const seedWords = qcsdk.newWalletSeedWords();
|
|
200
|
+
assert.ok(seedWords);
|
|
201
|
+
assert.ok(Array.isArray(seedWords) || typeof seedWords === 'string');
|
|
202
|
+
|
|
203
|
+
// Positive: open deterministic wallet from seed words fixture (48-word legacy / hybrideds)
|
|
204
|
+
const seedWallet = qcsdk.openWalletFromSeedWords(SEED_WORD_LIST);
|
|
205
|
+
assert.ok(seedWallet);
|
|
206
|
+
assert.equal(qcsdk.verifyWallet(seedWallet), true);
|
|
207
|
+
assert.equal(qcsdk.isAddressValid(seedWallet.address), true);
|
|
208
|
+
|
|
209
|
+
// Negative: invalid seed input
|
|
210
|
+
assert.equal(qcsdk.openWalletFromSeedWords(['not', 'enough']), null);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('seed words: openWalletFromSeedWords TEST_SEED_WORDS yields TEST_SEED_ADDRESS', () => {
|
|
214
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded and verifyWallet(newWallet()) must pass');
|
|
215
|
+
const wallet = qcsdk.openWalletFromSeedWords(TEST_SEED_WORDS);
|
|
216
|
+
assert.ok(wallet, 'openWalletFromSeedWords should return a wallet');
|
|
217
|
+
assert.equal(wallet.address.toLowerCase(), TEST_SEED_ADDRESS);
|
|
218
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
219
|
+
assert.equal(qcsdk.isAddressValid(wallet.address), true);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('seed words: first 32 words from TEST_SEED_WORDS — verifyWallet and isAddressValid (deterministic)', () => {
|
|
223
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded and verifyWallet(newWallet()) must pass');
|
|
224
|
+
const wallet = qcsdk.openWalletFromSeedWords(TEST_SEED_WORDS_32);
|
|
225
|
+
assert.ok(wallet, 'openWalletFromSeedWords(first 32) should return a wallet');
|
|
226
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
227
|
+
assert.equal(qcsdk.isAddressValid(wallet.address), true);
|
|
228
|
+
assert.equal(wallet.address.toLowerCase(), TEST_SEED_ADDRESS_32);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('seed words: first 36 words from TEST_SEED_WORDS — verifyWallet and isAddressValid (deterministic)', () => {
|
|
232
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded and verifyWallet(newWallet()) must pass');
|
|
233
|
+
const wallet = qcsdk.openWalletFromSeedWords(TEST_SEED_WORDS_36);
|
|
234
|
+
assert.ok(wallet, 'openWalletFromSeedWords(first 36) should return a wallet');
|
|
235
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
236
|
+
assert.equal(qcsdk.isAddressValid(wallet.address), true);
|
|
237
|
+
assert.equal(wallet.address.toLowerCase(), TEST_SEED_ADDRESS_36);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// --- openWalletFromSeed tests (raw seed byte arrays) ---
|
|
241
|
+
|
|
242
|
+
// Hardcoded seed byte arrays derived from TEST_SEED_WORDS via seed-words.getSeedArrayFromWordList
|
|
243
|
+
const TEST_SEED_ARRAY_48 = [49,159,218,142,198,66,182,182,73,216,5,119,6,71,216,42,164,55,124,237,92,81,228,227,156,0,38,189,152,58,215,177,80,252,71,86,51,210,70,33,106,200,184,26,246,139,249,41,191,104,163,253,21,26,43,108,146,94,243,204,112,236,219,139,218,249,224,255,76,150,203,7,108,119,101,70,217,112,225,190,112,169,98,168,104,223,14,235,161,192,118,167,128,203,76,59];
|
|
244
|
+
const TEST_SEED_ARRAY_32 = [49,159,218,142,198,66,182,182,73,216,5,119,6,71,216,42,164,55,124,237,92,81,228,227,156,0,38,189,152,58,215,177,80,252,71,86,51,210,70,33,106,200,184,26,246,139,249,41,191,104,163,253,21,26,43,108,146,94,243,204,112,236,219,139];
|
|
245
|
+
const TEST_SEED_ARRAY_36 = [49,159,218,142,198,66,182,182,73,216,5,119,6,71,216,42,164,55,124,237,92,81,228,227,156,0,38,189,152,58,215,177,80,252,71,86,51,210,70,33,106,200,184,26,246,139,249,41,191,104,163,253,21,26,43,108,146,94,243,204,112,236,219,139,218,249,224,255,76,150,203,7];
|
|
246
|
+
|
|
247
|
+
test('openWalletFromSeed: 96-byte seed produces expected address (hybrideds)', () => {
|
|
248
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded');
|
|
249
|
+
const wallet = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_48);
|
|
250
|
+
assert.ok(wallet, 'openWalletFromSeed should return a wallet for 96-byte seed');
|
|
251
|
+
assert.equal(wallet.address.toLowerCase(), TEST_SEED_ADDRESS);
|
|
252
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
253
|
+
assert.equal(qcsdk.isAddressValid(wallet.address), true);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test('openWalletFromSeed: 64-byte seed produces expected address (hybrid)', () => {
|
|
257
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded');
|
|
258
|
+
const wallet = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_32);
|
|
259
|
+
assert.ok(wallet, 'openWalletFromSeed should return a wallet for 64-byte seed');
|
|
260
|
+
assert.equal(wallet.address.toLowerCase(), TEST_SEED_ADDRESS_32);
|
|
261
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
262
|
+
assert.equal(qcsdk.isAddressValid(wallet.address), true);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('openWalletFromSeed: 72-byte seed produces expected address (hybrid5)', () => {
|
|
266
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded');
|
|
267
|
+
const wallet = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_36);
|
|
268
|
+
assert.ok(wallet, 'openWalletFromSeed should return a wallet for 72-byte seed');
|
|
269
|
+
assert.equal(wallet.address.toLowerCase(), TEST_SEED_ADDRESS_36);
|
|
270
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
271
|
+
assert.equal(qcsdk.isAddressValid(wallet.address), true);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test('openWalletFromSeed: matches openWalletFromSeedWords for all three schemes', () => {
|
|
275
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded');
|
|
276
|
+
for (const [seedArray, seedWords] of [
|
|
277
|
+
[TEST_SEED_ARRAY_48, TEST_SEED_WORDS],
|
|
278
|
+
[TEST_SEED_ARRAY_32, TEST_SEED_WORDS_32],
|
|
279
|
+
[TEST_SEED_ARRAY_36, TEST_SEED_WORDS_36],
|
|
280
|
+
]) {
|
|
281
|
+
const fromSeed = qcsdk.openWalletFromSeed(seedArray);
|
|
282
|
+
const fromWords = qcsdk.openWalletFromSeedWords(seedWords);
|
|
283
|
+
assert.ok(fromSeed, 'openWalletFromSeed should return a wallet');
|
|
284
|
+
assert.ok(fromWords, 'openWalletFromSeedWords should return a wallet');
|
|
285
|
+
assert.equal(fromSeed.address.toLowerCase(), fromWords.address.toLowerCase());
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test('openWalletFromSeed: accepts Uint8Array input', () => {
|
|
290
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded');
|
|
291
|
+
const wallet = qcsdk.openWalletFromSeed(new Uint8Array(TEST_SEED_ARRAY_48));
|
|
292
|
+
assert.ok(wallet, 'openWalletFromSeed should accept Uint8Array');
|
|
293
|
+
assert.equal(wallet.address.toLowerCase(), TEST_SEED_ADDRESS);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test('openWalletFromSeed: null input returns null', () => {
|
|
297
|
+
assert.equal(qcsdk.openWalletFromSeed(null), null);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test('openWalletFromSeed: undefined input returns null', () => {
|
|
301
|
+
assert.equal(qcsdk.openWalletFromSeed(undefined), null);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test('openWalletFromSeed: empty array returns null', () => {
|
|
305
|
+
assert.equal(qcsdk.openWalletFromSeed([]), null);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test('openWalletFromSeed: wrong length array returns null', () => {
|
|
309
|
+
assert.equal(qcsdk.openWalletFromSeed([1, 2, 3, 4, 5]), null);
|
|
310
|
+
assert.equal(qcsdk.openWalletFromSeed(new Array(63).fill(0)), null);
|
|
311
|
+
assert.equal(qcsdk.openWalletFromSeed(new Array(65).fill(0)), null);
|
|
312
|
+
assert.equal(qcsdk.openWalletFromSeed(new Array(71).fill(0)), null);
|
|
313
|
+
assert.equal(qcsdk.openWalletFromSeed(new Array(73).fill(0)), null);
|
|
314
|
+
assert.equal(qcsdk.openWalletFromSeed(new Array(95).fill(0)), null);
|
|
315
|
+
assert.equal(qcsdk.openWalletFromSeed(new Array(97).fill(0)), null);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test('openWalletFromSeed: non-array input returns null', () => {
|
|
319
|
+
assert.equal(qcsdk.openWalletFromSeed('not an array'), null);
|
|
320
|
+
assert.equal(qcsdk.openWalletFromSeed(12345), null);
|
|
321
|
+
assert.equal(qcsdk.openWalletFromSeed({}), null);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// --- serializeSeedAsEncryptedWallet tests ---
|
|
325
|
+
|
|
326
|
+
test('serializeSeedAsEncryptedWallet: 96-byte seed roundtrip — address and keys match', () => {
|
|
327
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded');
|
|
328
|
+
const json = qcsdk.serializeSeedAsEncryptedWallet(TEST_SEED_ARRAY_48, SEED_WALLET_TEST_PASSPHRASE);
|
|
329
|
+
assert.ok(json && typeof json === 'string', 'should return a JSON string');
|
|
330
|
+
const wallet = qcsdk.deserializeEncryptedWallet(json, SEED_WALLET_TEST_PASSPHRASE);
|
|
331
|
+
assert.ok(wallet, 'deserializeEncryptedWallet should return a wallet');
|
|
332
|
+
assert.equal(wallet.address.toLowerCase(), TEST_SEED_ADDRESS);
|
|
333
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
334
|
+
const ref = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_48);
|
|
335
|
+
assert.ok(ref);
|
|
336
|
+
assert.equal(wallet.publicKey.length, ref.publicKey.length);
|
|
337
|
+
for (let i = 0; i < wallet.publicKey.length; i++) assert.equal(wallet.publicKey[i], ref.publicKey[i]);
|
|
338
|
+
assert.equal(wallet.privateKey.length, ref.privateKey.length);
|
|
339
|
+
for (let i = 0; i < wallet.privateKey.length; i++) assert.equal(wallet.privateKey[i], ref.privateKey[i]);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test('serializeSeedAsEncryptedWallet: 64-byte seed roundtrip — address and keys match', () => {
|
|
343
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded');
|
|
344
|
+
const json = qcsdk.serializeSeedAsEncryptedWallet(TEST_SEED_ARRAY_32, SEED_WALLET_TEST_PASSPHRASE);
|
|
345
|
+
assert.ok(json && typeof json === 'string', 'should return a JSON string');
|
|
346
|
+
const wallet = qcsdk.deserializeEncryptedWallet(json, SEED_WALLET_TEST_PASSPHRASE);
|
|
347
|
+
assert.ok(wallet, 'deserializeEncryptedWallet should return a wallet');
|
|
348
|
+
assert.equal(wallet.address.toLowerCase(), TEST_SEED_ADDRESS_32);
|
|
349
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
350
|
+
const ref = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_32);
|
|
351
|
+
assert.ok(ref);
|
|
352
|
+
assert.equal(wallet.publicKey.length, ref.publicKey.length);
|
|
353
|
+
for (let i = 0; i < wallet.publicKey.length; i++) assert.equal(wallet.publicKey[i], ref.publicKey[i]);
|
|
354
|
+
assert.equal(wallet.privateKey.length, ref.privateKey.length);
|
|
355
|
+
for (let i = 0; i < wallet.privateKey.length; i++) assert.equal(wallet.privateKey[i], ref.privateKey[i]);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test('serializeSeedAsEncryptedWallet: 72-byte seed roundtrip — address and keys match', () => {
|
|
359
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded');
|
|
360
|
+
const json = qcsdk.serializeSeedAsEncryptedWallet(TEST_SEED_ARRAY_36, SEED_WALLET_TEST_PASSPHRASE);
|
|
361
|
+
assert.ok(json && typeof json === 'string', 'should return a JSON string');
|
|
362
|
+
const wallet = qcsdk.deserializeEncryptedWallet(json, SEED_WALLET_TEST_PASSPHRASE);
|
|
363
|
+
assert.ok(wallet, 'deserializeEncryptedWallet should return a wallet');
|
|
364
|
+
assert.equal(wallet.address.toLowerCase(), TEST_SEED_ADDRESS_36);
|
|
365
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
366
|
+
const ref = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_36);
|
|
367
|
+
assert.ok(ref);
|
|
368
|
+
assert.equal(wallet.publicKey.length, ref.publicKey.length);
|
|
369
|
+
for (let i = 0; i < wallet.publicKey.length; i++) assert.equal(wallet.publicKey[i], ref.publicKey[i]);
|
|
370
|
+
assert.equal(wallet.privateKey.length, ref.privateKey.length);
|
|
371
|
+
for (let i = 0; i < wallet.privateKey.length; i++) assert.equal(wallet.privateKey[i], ref.privateKey[i]);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test('serializeSeedAsEncryptedWallet: null seed returns null', () => {
|
|
375
|
+
assert.equal(qcsdk.serializeSeedAsEncryptedWallet(null, SEED_WALLET_TEST_PASSPHRASE), null);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test('serializeSeedAsEncryptedWallet: wrong-length seed returns null', () => {
|
|
379
|
+
assert.equal(qcsdk.serializeSeedAsEncryptedWallet(new Array(5).fill(0), SEED_WALLET_TEST_PASSPHRASE), null);
|
|
380
|
+
assert.equal(qcsdk.serializeSeedAsEncryptedWallet(new Array(63).fill(0), SEED_WALLET_TEST_PASSPHRASE), null);
|
|
381
|
+
assert.equal(qcsdk.serializeSeedAsEncryptedWallet(new Array(97).fill(0), SEED_WALLET_TEST_PASSPHRASE), null);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test('serializeSeedAsEncryptedWallet: null passphrase returns null', () => {
|
|
385
|
+
assert.equal(qcsdk.serializeSeedAsEncryptedWallet(TEST_SEED_ARRAY_32, null), null);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
test('serializeSeedAsEncryptedWallet: short passphrase returns null', () => {
|
|
389
|
+
assert.equal(qcsdk.serializeSeedAsEncryptedWallet(TEST_SEED_ARRAY_32, 'short'), null);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
test('serializeSeedAsEncryptedWallet: non-string passphrase returns null', () => {
|
|
393
|
+
assert.equal(qcsdk.serializeSeedAsEncryptedWallet(TEST_SEED_ARRAY_32, 12345), null);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// --- preExpansionSeed field tests ---
|
|
397
|
+
|
|
398
|
+
test('Wallet class: constructor without preExpansionSeed defaults to null', () => {
|
|
399
|
+
const w = new qcsdk.Wallet('0xabc', [1, 2], [3, 4]);
|
|
400
|
+
assert.equal(w.preExpansionSeed, null);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test('Wallet class: constructor with preExpansionSeed stores it', () => {
|
|
404
|
+
const seed = new Uint8Array([10, 20, 30]);
|
|
405
|
+
const w = new qcsdk.Wallet('0xabc', [1, 2], [3, 4], seed);
|
|
406
|
+
assert.ok(w.preExpansionSeed != null);
|
|
407
|
+
assert.equal(w.preExpansionSeed.length, 3);
|
|
408
|
+
assert.equal(w.preExpansionSeed[0], 10);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
test('newWallet: wallet has preExpansionSeed === null', () => {
|
|
412
|
+
const w = qcsdk.newWallet();
|
|
413
|
+
assert.ok(w && typeof w === 'object');
|
|
414
|
+
assert.equal(w.preExpansionSeed, null);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
test('openWalletFromSeed: 64-byte seed — preExpansionSeed is byte-equal to input', () => {
|
|
418
|
+
const wallet = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_32);
|
|
419
|
+
assert.ok(wallet && wallet.preExpansionSeed != null);
|
|
420
|
+
assert.equal(wallet.preExpansionSeed.length, 64);
|
|
421
|
+
const inputU8 = new Uint8Array(TEST_SEED_ARRAY_32);
|
|
422
|
+
for (let i = 0; i < 64; i++) assert.equal(wallet.preExpansionSeed[i], inputU8[i]);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test('openWalletFromSeed: 72-byte seed — preExpansionSeed is byte-equal to input', () => {
|
|
426
|
+
const wallet = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_36);
|
|
427
|
+
assert.ok(wallet && wallet.preExpansionSeed != null);
|
|
428
|
+
assert.equal(wallet.preExpansionSeed.length, 72);
|
|
429
|
+
const inputU8 = new Uint8Array(TEST_SEED_ARRAY_36);
|
|
430
|
+
for (let i = 0; i < 72; i++) assert.equal(wallet.preExpansionSeed[i], inputU8[i]);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test('openWalletFromSeed: 96-byte seed — preExpansionSeed is byte-equal to input', () => {
|
|
434
|
+
const wallet = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_48);
|
|
435
|
+
assert.ok(wallet && wallet.preExpansionSeed != null);
|
|
436
|
+
assert.equal(wallet.preExpansionSeed.length, 96);
|
|
437
|
+
const inputU8 = new Uint8Array(TEST_SEED_ARRAY_48);
|
|
438
|
+
for (let i = 0; i < 96; i++) assert.equal(wallet.preExpansionSeed[i], inputU8[i]);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
test('openWalletFromSeedWords: wallet has non-null preExpansionSeed', () => {
|
|
442
|
+
const wallet = qcsdk.openWalletFromSeedWords(TEST_SEED_WORDS);
|
|
443
|
+
assert.ok(wallet && wallet.preExpansionSeed != null);
|
|
444
|
+
assert.equal(wallet.preExpansionSeed.length, 96);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
test('serializeEncryptedWallet roundtrip: seed wallet (64-byte) preserves preExpansionSeed and produces V5', () => {
|
|
448
|
+
const wallet = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_32);
|
|
449
|
+
assert.ok(wallet && wallet.preExpansionSeed != null);
|
|
450
|
+
const json = qcsdk.serializeEncryptedWallet(wallet, SEED_WALLET_TEST_PASSPHRASE);
|
|
451
|
+
assert.ok(json && typeof json === 'string');
|
|
452
|
+
const parsed = JSON.parse(json);
|
|
453
|
+
assert.equal(parsed.version, 5, 'seed wallet should produce V5 encrypted JSON');
|
|
454
|
+
const restored = qcsdk.deserializeEncryptedWallet(json, SEED_WALLET_TEST_PASSPHRASE);
|
|
455
|
+
assert.ok(restored);
|
|
456
|
+
assert.equal(restored.address.toLowerCase(), wallet.address.toLowerCase());
|
|
457
|
+
assert.ok(restored.preExpansionSeed != null);
|
|
458
|
+
assert.equal(restored.preExpansionSeed.length, 64);
|
|
459
|
+
const origSeed = new Uint8Array(TEST_SEED_ARRAY_32);
|
|
460
|
+
for (let i = 0; i < 64; i++) assert.equal(restored.preExpansionSeed[i], origSeed[i]);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
test('serializeEncryptedWallet roundtrip: seed wallet (72-byte) preserves preExpansionSeed and produces V5', () => {
|
|
464
|
+
const wallet = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_36);
|
|
465
|
+
assert.ok(wallet && wallet.preExpansionSeed != null);
|
|
466
|
+
const json = qcsdk.serializeEncryptedWallet(wallet, SEED_WALLET_TEST_PASSPHRASE);
|
|
467
|
+
assert.ok(json && typeof json === 'string');
|
|
468
|
+
const parsed = JSON.parse(json);
|
|
469
|
+
assert.equal(parsed.version, 5, 'seed wallet should produce V5 encrypted JSON');
|
|
470
|
+
const restored = qcsdk.deserializeEncryptedWallet(json, SEED_WALLET_TEST_PASSPHRASE);
|
|
471
|
+
assert.ok(restored);
|
|
472
|
+
assert.equal(restored.address.toLowerCase(), wallet.address.toLowerCase());
|
|
473
|
+
assert.ok(restored.preExpansionSeed != null);
|
|
474
|
+
assert.equal(restored.preExpansionSeed.length, 72);
|
|
475
|
+
const origSeed = new Uint8Array(TEST_SEED_ARRAY_36);
|
|
476
|
+
for (let i = 0; i < 72; i++) assert.equal(restored.preExpansionSeed[i], origSeed[i]);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
test('serializeEncryptedWallet roundtrip: seed wallet (96-byte) preserves preExpansionSeed and produces V5', () => {
|
|
480
|
+
const wallet = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_48);
|
|
481
|
+
assert.ok(wallet && wallet.preExpansionSeed != null);
|
|
482
|
+
const json = qcsdk.serializeEncryptedWallet(wallet, SEED_WALLET_TEST_PASSPHRASE);
|
|
483
|
+
assert.ok(json && typeof json === 'string');
|
|
484
|
+
const parsed = JSON.parse(json);
|
|
485
|
+
assert.equal(parsed.version, 5, 'seed wallet should produce V5 encrypted JSON');
|
|
486
|
+
const restored = qcsdk.deserializeEncryptedWallet(json, SEED_WALLET_TEST_PASSPHRASE);
|
|
487
|
+
assert.ok(restored);
|
|
488
|
+
assert.equal(restored.address.toLowerCase(), wallet.address.toLowerCase());
|
|
489
|
+
assert.ok(restored.preExpansionSeed != null);
|
|
490
|
+
assert.equal(restored.preExpansionSeed.length, 96);
|
|
491
|
+
const origSeed = new Uint8Array(TEST_SEED_ARRAY_48);
|
|
492
|
+
for (let i = 0; i < 96; i++) assert.equal(restored.preExpansionSeed[i], origSeed[i]);
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
test('serializeEncryptedWallet roundtrip: non-seed wallet has null preExpansionSeed and produces V4', () => {
|
|
496
|
+
const wallet = qcsdk.newWallet();
|
|
497
|
+
assert.ok(wallet);
|
|
498
|
+
assert.equal(wallet.preExpansionSeed, null);
|
|
499
|
+
const json = qcsdk.serializeEncryptedWallet(wallet, SEED_WALLET_TEST_PASSPHRASE);
|
|
500
|
+
assert.ok(json && typeof json === 'string');
|
|
501
|
+
const parsed = JSON.parse(json);
|
|
502
|
+
assert.equal(parsed.version, 4, 'non-seed wallet should produce V4 encrypted JSON');
|
|
503
|
+
const restored = qcsdk.deserializeEncryptedWallet(json, SEED_WALLET_TEST_PASSPHRASE);
|
|
504
|
+
assert.ok(restored);
|
|
505
|
+
assert.equal(restored.address.toLowerCase(), wallet.address.toLowerCase());
|
|
506
|
+
assert.equal(restored.preExpansionSeed, null);
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test('serializeWallet/deserializeWallet roundtrip: seed wallet preserves preExpansionSeed', () => {
|
|
510
|
+
const wallet = qcsdk.openWalletFromSeed(TEST_SEED_ARRAY_32);
|
|
511
|
+
assert.ok(wallet && wallet.preExpansionSeed != null);
|
|
512
|
+
const json = qcsdk.serializeWallet(wallet);
|
|
513
|
+
assert.ok(json && typeof json === 'string');
|
|
514
|
+
const parsed = JSON.parse(json);
|
|
515
|
+
assert.ok(parsed.preExpansionSeed, 'serialized JSON should contain preExpansionSeed field');
|
|
516
|
+
const restored = qcsdk.deserializeWallet(json);
|
|
517
|
+
assert.ok(restored);
|
|
518
|
+
assert.equal(restored.address.toLowerCase(), wallet.address.toLowerCase());
|
|
519
|
+
assert.ok(restored.preExpansionSeed != null);
|
|
520
|
+
assert.equal(restored.preExpansionSeed.length, 64);
|
|
521
|
+
const origSeed = new Uint8Array(TEST_SEED_ARRAY_32);
|
|
522
|
+
for (let i = 0; i < 64; i++) assert.equal(restored.preExpansionSeed[i], origSeed[i]);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
test('serializeWallet/deserializeWallet roundtrip: non-seed wallet has null preExpansionSeed and no field in JSON', () => {
|
|
526
|
+
const wallet = qcsdk.newWallet();
|
|
527
|
+
assert.ok(wallet);
|
|
528
|
+
assert.equal(wallet.preExpansionSeed, null);
|
|
529
|
+
const json = qcsdk.serializeWallet(wallet);
|
|
530
|
+
assert.ok(json && typeof json === 'string');
|
|
531
|
+
const parsed = JSON.parse(json);
|
|
532
|
+
assert.equal(parsed.preExpansionSeed, undefined, 'non-seed wallet JSON should not contain preExpansionSeed');
|
|
533
|
+
const restored = qcsdk.deserializeWallet(json);
|
|
534
|
+
assert.ok(restored);
|
|
535
|
+
assert.equal(restored.preExpansionSeed, null);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
test('backward compat: deserializeEncryptedWallet on V4 JSON (no seed) returns wallet with null preExpansionSeed', () => {
|
|
539
|
+
const wallet = qcsdk.deserializeEncryptedWallet(EXAMPLE_WALLET_ENCRYPTED_JSON, EXAMPLE_WALLET_PASSPHRASE);
|
|
540
|
+
assert.ok(wallet);
|
|
541
|
+
assert.equal(wallet.preExpansionSeed, null);
|
|
542
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
test('backward compat: deserializeWallet on JSON without preExpansionSeed field returns null seed', () => {
|
|
546
|
+
const wallet = qcsdk.newWallet();
|
|
547
|
+
const jsonWithoutSeed = JSON.stringify({
|
|
548
|
+
address: wallet.address,
|
|
549
|
+
privateKey: Array.from(wallet.privateKey).map(b => String.fromCodePoint(b)).join(''),
|
|
550
|
+
publicKey: Array.from(wallet.publicKey).map(b => String.fromCodePoint(b)).join(''),
|
|
551
|
+
});
|
|
552
|
+
const w = qcsdk.newWallet();
|
|
553
|
+
const json = qcsdk.serializeWallet(w);
|
|
554
|
+
const parsed = JSON.parse(json);
|
|
555
|
+
delete parsed.preExpansionSeed;
|
|
556
|
+
const cleanJson = JSON.stringify(parsed);
|
|
557
|
+
const restored = qcsdk.deserializeWallet(cleanJson);
|
|
558
|
+
assert.ok(restored);
|
|
559
|
+
assert.equal(restored.preExpansionSeed, null);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
// Hardcoded encrypted wallet JSON (from openWalletFromSeedWords + serializeEncryptedWallet with SEED_WALLET_TEST_PASSPHRASE).
|
|
563
|
+
// Deserialize and verify addresses match. Uses fixtures in tests/ (encrypted-48.json, encrypted-32.json, encrypted-36.json).
|
|
564
|
+
test('seed words: deserializeEncryptedWallet from serialized 48/32/36 seed wallets — addresses match', () => {
|
|
565
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded and verifyWallet(newWallet()) must pass');
|
|
566
|
+
const testsDir = __dirname;
|
|
567
|
+
const enc48 = fs.readFileSync(path.join(testsDir, 'encrypted-48.json'), 'utf8').trim();
|
|
568
|
+
const enc32 = fs.readFileSync(path.join(testsDir, 'encrypted-32.json'), 'utf8').trim();
|
|
569
|
+
const enc36 = fs.readFileSync(path.join(testsDir, 'encrypted-36.json'), 'utf8').trim();
|
|
570
|
+
for (const [enc, expectedAddress] of [
|
|
571
|
+
[enc48, TEST_SEED_ADDRESS],
|
|
572
|
+
[enc32, TEST_SEED_ADDRESS_32],
|
|
573
|
+
[enc36, TEST_SEED_ADDRESS_36],
|
|
574
|
+
]) {
|
|
575
|
+
const wallet = qcsdk.deserializeEncryptedWallet(enc, SEED_WALLET_TEST_PASSPHRASE);
|
|
576
|
+
assert.ok(wallet, 'deserializeEncryptedWallet should return a wallet');
|
|
577
|
+
assert.equal(wallet.address.toLowerCase(), expectedAddress);
|
|
578
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
579
|
+
assert.equal(qcsdk.isAddressValid(wallet.address), true);
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
test('publicKeyFromPrivateKey/addressFromPublicKey and signature helpers', () => {
|
|
584
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded and verifyWallet(newWallet()) must pass');
|
|
585
|
+
const wallet = qcsdk.newWallet();
|
|
586
|
+
assert.ok(wallet);
|
|
587
|
+
assert.equal(qcsdk.verifyWallet(wallet), true);
|
|
588
|
+
|
|
589
|
+
const pubHex = qcsdk.publicKeyFromPrivateKey(wallet.privateKey);
|
|
590
|
+
assert.ok(pubHex && typeof pubHex === 'string');
|
|
591
|
+
assert.ok(isHex(pubHex), 'publicKeyFromPrivateKey returns hex (no 0x prefix)');
|
|
592
|
+
|
|
593
|
+
// Compare derived public key bytes to wallet publicKey bytes
|
|
594
|
+
const pubBytes = hexToBytes(pubHex);
|
|
595
|
+
assert.equal(pubBytes.length, wallet.publicKey.length);
|
|
596
|
+
for (let i = 0; i < pubBytes.length; i++) assert.equal(pubBytes[i], wallet.publicKey[i]);
|
|
597
|
+
|
|
598
|
+
// addressFromPublicKey should match wallet address
|
|
599
|
+
const addr = qcsdk.addressFromPublicKey(wallet.publicKey);
|
|
600
|
+
assert.equal(addr.toLowerCase(), wallet.address.toLowerCase());
|
|
601
|
+
|
|
602
|
+
// combinePublicKeySignature + publicKeyFromSignature roundtrip (use CIRCL hybrideds when available)
|
|
603
|
+
const digest = new TextEncoder().encode('verifyverifyverifyverifyverifyok'); // 32 bytes
|
|
604
|
+
assert.equal(digest.length, 32);
|
|
605
|
+
const g = typeof globalThis !== 'undefined' ? globalThis : typeof global !== 'undefined' ? global : {};
|
|
606
|
+
const circl = g.circl;
|
|
607
|
+
let sig;
|
|
608
|
+
if (circl && circl.hybrideds) {
|
|
609
|
+
const privU8 = wallet.privateKey instanceof Uint8Array ? wallet.privateKey : new Uint8Array(wallet.privateKey);
|
|
610
|
+
const sigRes = circl.hybrideds.signCompact(privU8, digest);
|
|
611
|
+
if (sigRes && sigRes.error) throw new Error('CIRCL sign failed: ' + sigRes.error);
|
|
612
|
+
sig = sigRes.result instanceof Uint8Array ? Array.from(sigRes.result) : sigRes.result;
|
|
613
|
+
} else {
|
|
614
|
+
// Skip roundtrip if CIRCL not loaded (e.g. WASM not yet CIRCL build)
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
const combinedHex = qcsdk.combinePublicKeySignature(wallet.publicKey, sig);
|
|
618
|
+
assert.ok(combinedHex && typeof combinedHex === 'string' && isHex(combinedHex));
|
|
619
|
+
|
|
620
|
+
const combinedBytes = hexToBytes(combinedHex);
|
|
621
|
+
const recoveredPubHex = qcsdk.publicKeyFromSignature(digest, combinedBytes);
|
|
622
|
+
assert.ok(recoveredPubHex && typeof recoveredPubHex === 'string' && isHex(recoveredPubHex));
|
|
623
|
+
assert.equal(recoveredPubHex.toLowerCase(), pubHex.toLowerCase());
|
|
624
|
+
|
|
625
|
+
// Negative: wrong digest size
|
|
626
|
+
assert.equal(qcsdk.publicKeyFromSignature([1, 2, 3], combinedBytes), null);
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
test('RLP: encodeRlp/decodeRlp roundtrip and negative decode', () => {
|
|
630
|
+
// Note: current WASM DecodeRlp returns a JSON string where decoded bytes appear as boolean arrays.
|
|
631
|
+
// We validate that encode->decode is structurally consistent rather than matching the original JS value.
|
|
632
|
+
const enc = qcsdk.encodeRlp(['hello', 123, true]);
|
|
633
|
+
assert.equal(enc.error, '');
|
|
634
|
+
assert.ok(isHex0x(enc.result));
|
|
635
|
+
|
|
636
|
+
const dec = qcsdk.decodeRlp(enc.result);
|
|
637
|
+
assert.equal(dec.error, '');
|
|
638
|
+
const decoded = JSON.parse(dec.result);
|
|
639
|
+
assert.ok(Array.isArray(decoded));
|
|
640
|
+
assert.equal(decoded.length, 3);
|
|
641
|
+
assert.ok(Array.isArray(decoded[0]) && decoded[0].length === 5); // "hello" bytes -> 5 trues
|
|
642
|
+
assert.ok(Array.isArray(decoded[1]) && decoded[1].length >= 1);
|
|
643
|
+
assert.ok(Array.isArray(decoded[2]) && decoded[2].length >= 1);
|
|
644
|
+
assert.ok(decoded.flat(2).every((b) => typeof b === 'boolean'));
|
|
645
|
+
|
|
646
|
+
const bad = qcsdk.decodeRlp('not-hex');
|
|
647
|
+
assert.notEqual(bad.error, '');
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
test('ABI pack/unpack: packMethodData/unpackMethodData via RPC eth_call', async () => {
|
|
651
|
+
// Minimal ERC20-like ABI used in examples
|
|
652
|
+
const erc20ABI = JSON.stringify([
|
|
653
|
+
{
|
|
654
|
+
name: 'balanceOf',
|
|
655
|
+
type: 'function',
|
|
656
|
+
inputs: [{ name: 'account', type: 'address' }],
|
|
657
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
658
|
+
stateMutability: 'view',
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
name: 'name',
|
|
662
|
+
type: 'function',
|
|
663
|
+
inputs: [],
|
|
664
|
+
outputs: [{ name: '', type: 'string' }],
|
|
665
|
+
stateMutability: 'view',
|
|
666
|
+
},
|
|
667
|
+
]);
|
|
668
|
+
|
|
669
|
+
const tokenAddress =
|
|
670
|
+
'0x1Bd75060B22686a9f32Af80BC02348c1BAeDBba06f47ad723885c92a6566B65d';
|
|
671
|
+
const holderAddress =
|
|
672
|
+
'0xd51773b5dde3f8e4d29ae42b5046510e2a11fd0c8e4175853d6227896eb445c6';
|
|
673
|
+
|
|
674
|
+
const pack = qcsdk.packMethodData(erc20ABI, 'balanceOf', holderAddress);
|
|
675
|
+
assert.equal(pack.error, '');
|
|
676
|
+
assert.ok(isHex0x(pack.result));
|
|
677
|
+
|
|
678
|
+
const call = await rpc('eth_call', [{ to: tokenAddress, data: pack.result }, 'latest']);
|
|
679
|
+
assert.ok(call && (call.result || call.error));
|
|
680
|
+
assert.ok(call.result && isHex0x(call.result), 'eth_call should return hex');
|
|
681
|
+
|
|
682
|
+
const unpack = qcsdk.unpackMethodData(erc20ABI, 'balanceOf', call.result);
|
|
683
|
+
assert.equal(unpack.error, '');
|
|
684
|
+
const parsed = JSON.parse(unpack.result);
|
|
685
|
+
assert.ok(Array.isArray(parsed));
|
|
686
|
+
assert.equal(typeof parsed[0], 'string');
|
|
687
|
+
|
|
688
|
+
// Negative: invalid ABI JSON
|
|
689
|
+
const badPack = qcsdk.packMethodData('not-json', 'balanceOf', holderAddress);
|
|
690
|
+
assert.notEqual(badPack.error, '');
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
test('Event logs: encodeEventLog/decodeEventLog (Transfer)', () => {
|
|
694
|
+
const transferABI = JSON.stringify([
|
|
695
|
+
{
|
|
696
|
+
name: 'Transfer',
|
|
697
|
+
type: 'event',
|
|
698
|
+
anonymous: false,
|
|
699
|
+
inputs: [
|
|
700
|
+
{ name: 'from', type: 'address', indexed: true },
|
|
701
|
+
{ name: 'to', type: 'address', indexed: true },
|
|
702
|
+
{ name: 'value', type: 'uint256', indexed: false },
|
|
703
|
+
],
|
|
704
|
+
},
|
|
705
|
+
]);
|
|
706
|
+
|
|
707
|
+
const fromAddress =
|
|
708
|
+
'0xd51773b5dde3f8e4d29ae42b5046510e2a11fd0c8e4175853d6227896eb445c6';
|
|
709
|
+
const toAddress =
|
|
710
|
+
'0x1Bd75060B22686a9f32Af80BC02348c1BAeDBba06f47ad723885c92a6566B65d';
|
|
711
|
+
const value = '1000000000000000000';
|
|
712
|
+
|
|
713
|
+
const enc = qcsdk.encodeEventLog(transferABI, 'Transfer', fromAddress, toAddress, value);
|
|
714
|
+
assert.equal(enc.error, '');
|
|
715
|
+
assert.ok(enc.result);
|
|
716
|
+
assert.equal(Array.isArray(enc.result.topics), true);
|
|
717
|
+
assert.equal(enc.result.topics.length, 3);
|
|
718
|
+
assert.ok(isHex0x(enc.result.data));
|
|
719
|
+
|
|
720
|
+
const dec = qcsdk.decodeEventLog(transferABI, 'Transfer', enc.result.topics, enc.result.data);
|
|
721
|
+
assert.equal(dec.error, '');
|
|
722
|
+
const decoded = JSON.parse(dec.result);
|
|
723
|
+
assert.equal(decoded.from.toLowerCase(), fromAddress.toLowerCase());
|
|
724
|
+
assert.equal(decoded.to.toLowerCase(), toAddress.toLowerCase());
|
|
725
|
+
assert.equal(decoded.value, value);
|
|
726
|
+
|
|
727
|
+
// Negative: invalid ABI
|
|
728
|
+
const bad = qcsdk.decodeEventLog('not-json', 'Transfer', enc.result.topics, enc.result.data);
|
|
729
|
+
assert.notEqual(bad.error, '');
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
// --- packCreateContractData tests (ported from Go main_test.go) ---
|
|
733
|
+
|
|
734
|
+
test('packCreateContractData: parameterless constructor', () => {
|
|
735
|
+
const abi = JSON.stringify([{
|
|
736
|
+
type: 'constructor',
|
|
737
|
+
inputs: [],
|
|
738
|
+
stateMutability: 'nonpayable',
|
|
739
|
+
}]);
|
|
740
|
+
const bytecode = '0x6080604052348015600f57600080fd5b506004361060325760003560e01c8063';
|
|
741
|
+
const result = qcsdk.packCreateContractData(abi, bytecode);
|
|
742
|
+
assert.equal(result.error, '');
|
|
743
|
+
assert.ok(isHex0x(result.result));
|
|
744
|
+
assert.ok(result.result.toLowerCase().startsWith(bytecode.toLowerCase()));
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
test('packCreateContractData: constructor with uint256 params', () => {
|
|
748
|
+
const abi = JSON.stringify([{
|
|
749
|
+
type: 'constructor',
|
|
750
|
+
inputs: [
|
|
751
|
+
{ name: 'a', type: 'uint256' },
|
|
752
|
+
{ name: 'b', type: 'uint256' },
|
|
753
|
+
],
|
|
754
|
+
stateMutability: 'nonpayable',
|
|
755
|
+
}]);
|
|
756
|
+
const bytecode = '0x6080604052348015600f57600080fd5b506004361060325760003560e01c8063';
|
|
757
|
+
const result = qcsdk.packCreateContractData(abi, bytecode, '1', '2');
|
|
758
|
+
assert.equal(result.error, '');
|
|
759
|
+
assert.ok(isHex0x(result.result));
|
|
760
|
+
assert.ok(result.result.length > bytecode.length, 'result should be longer than bytecode alone');
|
|
761
|
+
assert.ok(result.result.toLowerCase().startsWith(bytecode.toLowerCase()));
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
test('packCreateContractData: constructor with address param', () => {
|
|
765
|
+
const abi = JSON.stringify([{
|
|
766
|
+
type: 'constructor',
|
|
767
|
+
inputs: [{ name: 'owner', type: 'address' }],
|
|
768
|
+
stateMutability: 'nonpayable',
|
|
769
|
+
}]);
|
|
770
|
+
const bytecode = '0x6080604052348015600f57600080fd5b506004361060325760003560e01c8063';
|
|
771
|
+
const ownerAddr = '0x0000000000000000000000000000000000000000000000000000000000000001';
|
|
772
|
+
const result = qcsdk.packCreateContractData(abi, bytecode, ownerAddr);
|
|
773
|
+
assert.equal(result.error, '');
|
|
774
|
+
assert.ok(isHex0x(result.result));
|
|
775
|
+
assert.ok(result.result.length > bytecode.length);
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
test('packCreateContractData: constructor with uint256[] array param', () => {
|
|
779
|
+
const abi = JSON.stringify([{
|
|
780
|
+
type: 'constructor',
|
|
781
|
+
inputs: [{ name: 'values', type: 'uint256[]' }],
|
|
782
|
+
stateMutability: 'nonpayable',
|
|
783
|
+
}]);
|
|
784
|
+
const bytecode = '0x6080604052348015600f57600080fd5b506004361060325760003560e01c8063';
|
|
785
|
+
const result = qcsdk.packCreateContractData(abi, bytecode, ['100', '200', '300']);
|
|
786
|
+
assert.equal(result.error, '');
|
|
787
|
+
assert.ok(isHex0x(result.result));
|
|
788
|
+
assert.ok(result.result.length > bytecode.length);
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
test('packCreateContractData: empty ABI JSON returns error', () => {
|
|
792
|
+
const bytecode = '0x6080604052348015600f57600080fd5b506004361060325760003560e01c8063';
|
|
793
|
+
const r1 = qcsdk.packCreateContractData('', bytecode);
|
|
794
|
+
assert.notEqual(r1.error, '');
|
|
795
|
+
const r2 = qcsdk.packCreateContractData(' ', bytecode);
|
|
796
|
+
assert.notEqual(r2.error, '');
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
test('packCreateContractData: empty bytecode returns error', () => {
|
|
800
|
+
const abi = JSON.stringify([{ type: 'constructor', inputs: [], stateMutability: 'nonpayable' }]);
|
|
801
|
+
const r1 = qcsdk.packCreateContractData(abi, '');
|
|
802
|
+
assert.notEqual(r1.error, '');
|
|
803
|
+
const r2 = qcsdk.packCreateContractData(abi, ' ');
|
|
804
|
+
assert.notEqual(r2.error, '');
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
test('packCreateContractData: invalid bytecode returns error', () => {
|
|
808
|
+
const abi = JSON.stringify([{ type: 'constructor', inputs: [], stateMutability: 'nonpayable' }]);
|
|
809
|
+
const result = qcsdk.packCreateContractData(abi, '0xinvalid');
|
|
810
|
+
assert.notEqual(result.error, '');
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
test('packCreateContractData: argument count mismatch returns error', () => {
|
|
814
|
+
const abi = JSON.stringify([{
|
|
815
|
+
type: 'constructor',
|
|
816
|
+
inputs: [
|
|
817
|
+
{ name: 'a', type: 'uint256' },
|
|
818
|
+
{ name: 'b', type: 'uint256' },
|
|
819
|
+
],
|
|
820
|
+
stateMutability: 'nonpayable',
|
|
821
|
+
}]);
|
|
822
|
+
const bytecode = '0x6080604052348015600f57600080fd5b506004361060325760003560e01c8063';
|
|
823
|
+
const tooFew = qcsdk.packCreateContractData(abi, bytecode, '1');
|
|
824
|
+
assert.notEqual(tooFew.error, '');
|
|
825
|
+
const tooMany = qcsdk.packCreateContractData(abi, bytecode, '1', '2', '3');
|
|
826
|
+
assert.notEqual(tooMany.error, '');
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
// --- packMethodData / unpackMethodData extended tests (ported from Go) ---
|
|
830
|
+
|
|
831
|
+
test('packMethodData: zero-argument method (name())', () => {
|
|
832
|
+
const abi = JSON.stringify([{
|
|
833
|
+
name: 'name',
|
|
834
|
+
type: 'function',
|
|
835
|
+
inputs: [],
|
|
836
|
+
outputs: [{ name: '', type: 'string' }],
|
|
837
|
+
stateMutability: 'view',
|
|
838
|
+
}]);
|
|
839
|
+
const result = qcsdk.packMethodData(abi, 'name');
|
|
840
|
+
assert.equal(result.error, '');
|
|
841
|
+
assert.ok(isHex0x(result.result));
|
|
842
|
+
assert.equal(result.result.length, 10, 'zero-arg method should produce 4-byte selector (0x + 8 hex chars)');
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
test('packMethodData: address[] argument', () => {
|
|
846
|
+
const abi = JSON.stringify([{
|
|
847
|
+
name: 'getAmountsIn',
|
|
848
|
+
type: 'function',
|
|
849
|
+
inputs: [
|
|
850
|
+
{ name: 'amountOut', type: 'uint256' },
|
|
851
|
+
{ name: 'path', type: 'address[]' },
|
|
852
|
+
],
|
|
853
|
+
outputs: [{ name: 'amounts', type: 'uint256[]' }],
|
|
854
|
+
stateMutability: 'view',
|
|
855
|
+
}]);
|
|
856
|
+
const addr1 = '0x0000000000000000000000000000000000000000000000000000000000000001';
|
|
857
|
+
const addr2 = '0x0000000000000000000000000000000000000000000000000000000000000002';
|
|
858
|
+
const result = qcsdk.packMethodData(abi, 'getAmountsIn', '1000', [addr1, addr2]);
|
|
859
|
+
assert.equal(result.error, '');
|
|
860
|
+
assert.ok(isHex0x(result.result));
|
|
861
|
+
assert.ok(result.result.length > 10);
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
test('packMethodData: uint256[] argument', () => {
|
|
865
|
+
const abi = JSON.stringify([{
|
|
866
|
+
name: 'batchTransfer',
|
|
867
|
+
type: 'function',
|
|
868
|
+
inputs: [{ name: 'amounts', type: 'uint256[]' }],
|
|
869
|
+
outputs: [],
|
|
870
|
+
stateMutability: 'nonpayable',
|
|
871
|
+
}]);
|
|
872
|
+
const result = qcsdk.packMethodData(abi, 'batchTransfer', ['100', '200', '300']);
|
|
873
|
+
assert.equal(result.error, '');
|
|
874
|
+
assert.ok(isHex0x(result.result));
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
test('packMethodData: mixed arguments (address, uint256, bool)', () => {
|
|
878
|
+
const abi = JSON.stringify([{
|
|
879
|
+
name: 'doSomething',
|
|
880
|
+
type: 'function',
|
|
881
|
+
inputs: [
|
|
882
|
+
{ name: 'to', type: 'address' },
|
|
883
|
+
{ name: 'amount', type: 'uint256' },
|
|
884
|
+
{ name: 'flag', type: 'bool' },
|
|
885
|
+
],
|
|
886
|
+
outputs: [],
|
|
887
|
+
stateMutability: 'nonpayable',
|
|
888
|
+
}]);
|
|
889
|
+
const addr = '0x0000000000000000000000000000000000000000000000000000000000000001';
|
|
890
|
+
const result = qcsdk.packMethodData(abi, 'doSomething', addr, '500', true);
|
|
891
|
+
assert.equal(result.error, '');
|
|
892
|
+
assert.ok(isHex0x(result.result));
|
|
893
|
+
assert.ok(result.result.length > 10);
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
test('packMethodData: empty method name returns error', () => {
|
|
897
|
+
const abi = JSON.stringify([{
|
|
898
|
+
name: 'getValue',
|
|
899
|
+
type: 'function',
|
|
900
|
+
inputs: [],
|
|
901
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
902
|
+
}]);
|
|
903
|
+
const r1 = qcsdk.packMethodData(abi, '');
|
|
904
|
+
assert.notEqual(r1.error, '');
|
|
905
|
+
const r2 = qcsdk.packMethodData(abi, ' ');
|
|
906
|
+
assert.notEqual(r2.error, '');
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
test('unpackMethodData: empty method name returns error', () => {
|
|
910
|
+
const abi = JSON.stringify([{
|
|
911
|
+
name: 'getValue',
|
|
912
|
+
type: 'function',
|
|
913
|
+
inputs: [],
|
|
914
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
915
|
+
}]);
|
|
916
|
+
const r1 = qcsdk.unpackMethodData(abi, '', '0x00');
|
|
917
|
+
assert.notEqual(r1.error, '');
|
|
918
|
+
const r2 = qcsdk.unpackMethodData(abi, ' ', '0x00');
|
|
919
|
+
assert.notEqual(r2.error, '');
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
test('unpackMethodData: non-existent method returns error', () => {
|
|
923
|
+
const abi = JSON.stringify([{
|
|
924
|
+
name: 'getValue',
|
|
925
|
+
type: 'function',
|
|
926
|
+
inputs: [],
|
|
927
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
928
|
+
}]);
|
|
929
|
+
const result = qcsdk.unpackMethodData(abi, 'nonExistentMethod', '0x00');
|
|
930
|
+
assert.notEqual(result.error, '');
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
test('unpackMethodData: invalid hex data returns error', () => {
|
|
934
|
+
const abi = JSON.stringify([{
|
|
935
|
+
name: 'getValue',
|
|
936
|
+
type: 'function',
|
|
937
|
+
inputs: [],
|
|
938
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
939
|
+
}]);
|
|
940
|
+
const result = qcsdk.unpackMethodData(abi, 'getValue', 'invalid hex');
|
|
941
|
+
assert.notEqual(result.error, '');
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
test('packMethodData/unpackMethodData: offline roundtrip with balanceOf', () => {
|
|
945
|
+
const abi = JSON.stringify([{
|
|
946
|
+
name: 'balanceOf',
|
|
947
|
+
type: 'function',
|
|
948
|
+
inputs: [{ name: 'account', type: 'address' }],
|
|
949
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
950
|
+
stateMutability: 'view',
|
|
951
|
+
}]);
|
|
952
|
+
const addr = '0x0000000000000000000000000000000000000000000000000000000000000001';
|
|
953
|
+
const pack = qcsdk.packMethodData(abi, 'balanceOf', addr);
|
|
954
|
+
assert.equal(pack.error, '');
|
|
955
|
+
assert.ok(isHex0x(pack.result));
|
|
956
|
+
const returnData = '0x' + '00'.repeat(31) + '0a';
|
|
957
|
+
const unpack = qcsdk.unpackMethodData(abi, 'balanceOf', returnData);
|
|
958
|
+
assert.equal(unpack.error, '');
|
|
959
|
+
const parsed = JSON.parse(unpack.result);
|
|
960
|
+
assert.ok(Array.isArray(parsed));
|
|
961
|
+
assert.equal(parsed[0], '10');
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
// --- Event log encode/decode extended tests (ported from Go) ---
|
|
965
|
+
|
|
966
|
+
test('encodeEventLog: all-indexed event (Approval)', () => {
|
|
967
|
+
const abi = JSON.stringify([{
|
|
968
|
+
name: 'Approval',
|
|
969
|
+
type: 'event',
|
|
970
|
+
anonymous: false,
|
|
971
|
+
inputs: [
|
|
972
|
+
{ name: 'owner', type: 'address', indexed: true },
|
|
973
|
+
{ name: 'spender', type: 'address', indexed: true },
|
|
974
|
+
{ name: 'value', type: 'uint256', indexed: true },
|
|
975
|
+
],
|
|
976
|
+
}]);
|
|
977
|
+
const owner = '0x0000000000000000000000000000000000000000000000000000000000000001';
|
|
978
|
+
const spender = '0x0000000000000000000000000000000000000000000000000000000000000002';
|
|
979
|
+
const value = '1000000000000000000';
|
|
980
|
+
const enc = qcsdk.encodeEventLog(abi, 'Approval', owner, spender, value);
|
|
981
|
+
assert.equal(enc.error, '');
|
|
982
|
+
assert.equal(enc.result.topics.length, 4);
|
|
983
|
+
assert.equal(enc.result.data, '0x');
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
test('encodeEventLog: all-non-indexed event (Received)', () => {
|
|
987
|
+
const abi = JSON.stringify([{
|
|
988
|
+
name: 'Received',
|
|
989
|
+
type: 'event',
|
|
990
|
+
anonymous: false,
|
|
991
|
+
inputs: [
|
|
992
|
+
{ name: 'sender', type: 'address', indexed: false },
|
|
993
|
+
{ name: 'amount', type: 'uint256', indexed: false },
|
|
994
|
+
],
|
|
995
|
+
}]);
|
|
996
|
+
const sender = '0x0000000000000000000000000000000000000000000000000000000000000001';
|
|
997
|
+
const amount = '1000000000000000000';
|
|
998
|
+
const enc = qcsdk.encodeEventLog(abi, 'Received', sender, amount);
|
|
999
|
+
assert.equal(enc.error, '');
|
|
1000
|
+
assert.equal(enc.result.topics.length, 1);
|
|
1001
|
+
assert.ok(enc.result.data !== '' && enc.result.data !== '0x');
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
test('encodeEventLog: anonymous event has no signature topic', () => {
|
|
1005
|
+
const abi = JSON.stringify([{
|
|
1006
|
+
name: 'AnonEvent',
|
|
1007
|
+
type: 'event',
|
|
1008
|
+
anonymous: true,
|
|
1009
|
+
inputs: [
|
|
1010
|
+
{ name: 'value', type: 'uint256', indexed: true },
|
|
1011
|
+
],
|
|
1012
|
+
}]);
|
|
1013
|
+
const enc = qcsdk.encodeEventLog(abi, 'AnonEvent', '42');
|
|
1014
|
+
assert.equal(enc.error, '');
|
|
1015
|
+
assert.equal(enc.result.topics.length, 1, 'anonymous event should have only indexed param topics, no signature');
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
test('encodeEventLog: invalid event name returns error', () => {
|
|
1019
|
+
const abi = JSON.stringify([{
|
|
1020
|
+
name: 'Transfer',
|
|
1021
|
+
type: 'event',
|
|
1022
|
+
anonymous: false,
|
|
1023
|
+
inputs: [],
|
|
1024
|
+
}]);
|
|
1025
|
+
const enc = qcsdk.encodeEventLog(abi, 'NonExistentEvent');
|
|
1026
|
+
assert.notEqual(enc.error, '');
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
test('encodeEventLog: argument count mismatch returns error', () => {
|
|
1030
|
+
const abi = JSON.stringify([{
|
|
1031
|
+
name: 'Transfer',
|
|
1032
|
+
type: 'event',
|
|
1033
|
+
anonymous: false,
|
|
1034
|
+
inputs: [
|
|
1035
|
+
{ name: 'from', type: 'address', indexed: true },
|
|
1036
|
+
{ name: 'to', type: 'address', indexed: true },
|
|
1037
|
+
],
|
|
1038
|
+
}]);
|
|
1039
|
+
const enc = qcsdk.encodeEventLog(abi, 'Transfer', '0x0000000000000000000000000000000000000000000000000000000000000001');
|
|
1040
|
+
assert.notEqual(enc.error, '');
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
test('decodeEventLog: invalid event name returns error', () => {
|
|
1044
|
+
const abi = JSON.stringify([{
|
|
1045
|
+
name: 'Transfer',
|
|
1046
|
+
type: 'event',
|
|
1047
|
+
anonymous: false,
|
|
1048
|
+
inputs: [],
|
|
1049
|
+
}]);
|
|
1050
|
+
const dec = qcsdk.decodeEventLog(abi, 'NonExistentEvent', ['0x00'], '0x');
|
|
1051
|
+
assert.notEqual(dec.error, '');
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
test('decodeEventLog: invalid topic signature returns error', () => {
|
|
1055
|
+
const abi = JSON.stringify([{
|
|
1056
|
+
name: 'Transfer',
|
|
1057
|
+
type: 'event',
|
|
1058
|
+
anonymous: false,
|
|
1059
|
+
inputs: [
|
|
1060
|
+
{ name: 'from', type: 'address', indexed: true },
|
|
1061
|
+
],
|
|
1062
|
+
}]);
|
|
1063
|
+
const wrongTopics = [
|
|
1064
|
+
'0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
1065
|
+
'0x0000000000000000000000000000000000000000000000000000000000000001',
|
|
1066
|
+
];
|
|
1067
|
+
const dec = qcsdk.decodeEventLog(abi, 'Transfer', wrongTopics, '0x');
|
|
1068
|
+
assert.notEqual(dec.error, '');
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
test('encodeEventLog/decodeEventLog: complex roundtrip (2 indexed + 3 non-indexed)', () => {
|
|
1072
|
+
const abi = JSON.stringify([{
|
|
1073
|
+
name: 'ComplexEvent',
|
|
1074
|
+
type: 'event',
|
|
1075
|
+
anonymous: false,
|
|
1076
|
+
inputs: [
|
|
1077
|
+
{ name: 'indexedAddr', type: 'address', indexed: true },
|
|
1078
|
+
{ name: 'indexedValue', type: 'uint256', indexed: true },
|
|
1079
|
+
{ name: 'nonIndexedAddr', type: 'address', indexed: false },
|
|
1080
|
+
{ name: 'nonIndexedValue', type: 'uint256', indexed: false },
|
|
1081
|
+
{ name: 'flag', type: 'bool', indexed: false },
|
|
1082
|
+
],
|
|
1083
|
+
}]);
|
|
1084
|
+
const indexedAddr = '0x0000000000000000000000000000000000000000000000000000000000000001';
|
|
1085
|
+
const indexedValue = '1000000000000000000';
|
|
1086
|
+
const nonIndexedAddr = '0x0000000000000000000000000000000000000000000000000000000000000002';
|
|
1087
|
+
const nonIndexedValue = '2000000000000000000';
|
|
1088
|
+
const flag = true;
|
|
1089
|
+
|
|
1090
|
+
const enc = qcsdk.encodeEventLog(abi, 'ComplexEvent', indexedAddr, indexedValue, nonIndexedAddr, nonIndexedValue, flag);
|
|
1091
|
+
assert.equal(enc.error, '');
|
|
1092
|
+
assert.equal(enc.result.topics.length, 3);
|
|
1093
|
+
assert.ok(enc.result.data !== '0x');
|
|
1094
|
+
|
|
1095
|
+
const dec = qcsdk.decodeEventLog(abi, 'ComplexEvent', enc.result.topics, enc.result.data);
|
|
1096
|
+
assert.equal(dec.error, '');
|
|
1097
|
+
const decoded = JSON.parse(dec.result);
|
|
1098
|
+
assert.equal(decoded.indexedAddr.toLowerCase(), indexedAddr.toLowerCase());
|
|
1099
|
+
assert.equal(decoded.indexedValue, indexedValue);
|
|
1100
|
+
assert.equal(decoded.nonIndexedAddr.toLowerCase(), nonIndexedAddr.toLowerCase());
|
|
1101
|
+
assert.equal(decoded.nonIndexedValue, nonIndexedValue);
|
|
1102
|
+
assert.equal(decoded.flag, true);
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
// --- RLP encode/decode extended tests (ported from Go) ---
|
|
1106
|
+
|
|
1107
|
+
test('encodeRlp/decodeRlp: string roundtrip', () => {
|
|
1108
|
+
const enc = qcsdk.encodeRlp('hello');
|
|
1109
|
+
assert.equal(enc.error, '');
|
|
1110
|
+
assert.ok(isHex0x(enc.result));
|
|
1111
|
+
const dec = qcsdk.decodeRlp(enc.result);
|
|
1112
|
+
assert.equal(dec.error, '');
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
test('encodeRlp/decodeRlp: number roundtrip', () => {
|
|
1116
|
+
const enc = qcsdk.encodeRlp(42);
|
|
1117
|
+
assert.equal(enc.error, '');
|
|
1118
|
+
assert.ok(isHex0x(enc.result));
|
|
1119
|
+
const dec = qcsdk.decodeRlp(enc.result);
|
|
1120
|
+
assert.equal(dec.error, '');
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
test('encodeRlp/decodeRlp: boolean roundtrip', () => {
|
|
1124
|
+
const encTrue = qcsdk.encodeRlp(true);
|
|
1125
|
+
assert.equal(encTrue.error, '');
|
|
1126
|
+
assert.ok(isHex0x(encTrue.result));
|
|
1127
|
+
const decTrue = qcsdk.decodeRlp(encTrue.result);
|
|
1128
|
+
assert.equal(decTrue.error, '');
|
|
1129
|
+
|
|
1130
|
+
const encFalse = qcsdk.encodeRlp(false);
|
|
1131
|
+
assert.equal(encFalse.error, '');
|
|
1132
|
+
assert.ok(isHex0x(encFalse.result));
|
|
1133
|
+
const decFalse = qcsdk.decodeRlp(encFalse.result);
|
|
1134
|
+
assert.equal(decFalse.error, '');
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
test('encodeRlp/decodeRlp: hex string (0x48656c6c6f = "Hello")', () => {
|
|
1138
|
+
const enc = qcsdk.encodeRlp('0x48656c6c6f');
|
|
1139
|
+
assert.equal(enc.error, '');
|
|
1140
|
+
assert.ok(isHex0x(enc.result));
|
|
1141
|
+
const dec = qcsdk.decodeRlp(enc.result);
|
|
1142
|
+
assert.equal(dec.error, '');
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
test('encodeRlp/decodeRlp: empty array', () => {
|
|
1146
|
+
const enc = qcsdk.encodeRlp([]);
|
|
1147
|
+
assert.equal(enc.error, '');
|
|
1148
|
+
assert.ok(isHex0x(enc.result));
|
|
1149
|
+
const dec = qcsdk.decodeRlp(enc.result);
|
|
1150
|
+
assert.equal(dec.error, '');
|
|
1151
|
+
const decoded = JSON.parse(dec.result);
|
|
1152
|
+
assert.ok(Array.isArray(decoded));
|
|
1153
|
+
assert.equal(decoded.length, 0);
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
test('encodeRlp/decodeRlp: nested array', () => {
|
|
1157
|
+
const enc = qcsdk.encodeRlp([['inner', 1], 'outer']);
|
|
1158
|
+
assert.equal(enc.error, '');
|
|
1159
|
+
assert.ok(isHex0x(enc.result));
|
|
1160
|
+
const dec = qcsdk.decodeRlp(enc.result);
|
|
1161
|
+
assert.equal(dec.error, '');
|
|
1162
|
+
const decoded = JSON.parse(dec.result);
|
|
1163
|
+
assert.ok(Array.isArray(decoded));
|
|
1164
|
+
assert.equal(decoded.length, 2);
|
|
1165
|
+
assert.ok(Array.isArray(decoded[0]));
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
|
+
test('decodeRlp: empty string input returns error', () => {
|
|
1169
|
+
const dec = qcsdk.decodeRlp('');
|
|
1170
|
+
assert.notEqual(dec.error, '');
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
// --- createAddress / createAddress2 extended tests (ported from Go) ---
|
|
1174
|
+
|
|
1175
|
+
test('createAddress: different nonces produce different addresses', () => {
|
|
1176
|
+
const w = qcsdk.newWallet();
|
|
1177
|
+
const addresses = new Set();
|
|
1178
|
+
for (let nonce = 0; nonce < 5; nonce++) {
|
|
1179
|
+
const addr = qcsdk.createAddress(w.address, nonce);
|
|
1180
|
+
assert.ok(addr && isHex0x(addr));
|
|
1181
|
+
addresses.add(addr.toLowerCase());
|
|
1182
|
+
}
|
|
1183
|
+
assert.equal(addresses.size, 5, 'nonces 0-4 should produce 5 distinct addresses');
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
test('createAddress: different deployer addresses produce different contract addresses', () => {
|
|
1187
|
+
const w1 = qcsdk.newWallet();
|
|
1188
|
+
const w2 = qcsdk.newWallet();
|
|
1189
|
+
const a1 = qcsdk.createAddress(w1.address, 0);
|
|
1190
|
+
const a2 = qcsdk.createAddress(w2.address, 0);
|
|
1191
|
+
assert.ok(a1 && a2);
|
|
1192
|
+
assert.notEqual(a1.toLowerCase(), a2.toLowerCase());
|
|
1193
|
+
});
|
|
1194
|
+
|
|
1195
|
+
test('createAddress2: different salts produce different addresses', () => {
|
|
1196
|
+
const w = qcsdk.newWallet();
|
|
1197
|
+
const initHash = '0x' + '11'.repeat(32);
|
|
1198
|
+
const salt1 = '0x' + '01'.repeat(32);
|
|
1199
|
+
const salt2 = '0x' + '02'.repeat(32);
|
|
1200
|
+
const a1 = qcsdk.createAddress2(w.address, salt1, initHash);
|
|
1201
|
+
const a2 = qcsdk.createAddress2(w.address, salt2, initHash);
|
|
1202
|
+
assert.ok(a1 && a2);
|
|
1203
|
+
assert.notEqual(a1.toLowerCase(), a2.toLowerCase());
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
test('createAddress2: different initHashes produce different addresses', () => {
|
|
1207
|
+
const w = qcsdk.newWallet();
|
|
1208
|
+
const salt = '0x' + '22'.repeat(32);
|
|
1209
|
+
const initHash1 = '0x' + '11'.repeat(32);
|
|
1210
|
+
const initHash2 = '0x' + '33'.repeat(32);
|
|
1211
|
+
const a1 = qcsdk.createAddress2(w.address, salt, initHash1);
|
|
1212
|
+
const a2 = qcsdk.createAddress2(w.address, salt, initHash2);
|
|
1213
|
+
assert.ok(a1 && a2);
|
|
1214
|
+
assert.notEqual(a1.toLowerCase(), a2.toLowerCase());
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
test('contract address calculation: createAddress + createAddress2 basic validity and determinism', () => {
|
|
1218
|
+
const w = qcsdk.newWallet();
|
|
1219
|
+
const a0 = qcsdk.createAddress(w.address, 0);
|
|
1220
|
+
const a0b = qcsdk.createAddress(w.address, 0);
|
|
1221
|
+
const a1 = qcsdk.createAddress(w.address, 1);
|
|
1222
|
+
assert.ok(a0 && typeof a0 === 'string');
|
|
1223
|
+
assert.ok(isHex0x(a0));
|
|
1224
|
+
assert.equal(a0, a0b);
|
|
1225
|
+
assert.notEqual(a0, a1);
|
|
1226
|
+
|
|
1227
|
+
// CREATE2
|
|
1228
|
+
const salt = '0x' + '22'.repeat(32);
|
|
1229
|
+
const initHash = '0x' + '11'.repeat(32);
|
|
1230
|
+
const c2a = qcsdk.createAddress2(w.address, salt, initHash);
|
|
1231
|
+
const c2b = qcsdk.createAddress2(w.address, salt, initHash);
|
|
1232
|
+
assert.ok(c2a && isHex0x(c2a));
|
|
1233
|
+
assert.equal(c2a, c2b);
|
|
1234
|
+
|
|
1235
|
+
// Negative: invalid nonce type / invalid params
|
|
1236
|
+
assert.equal(qcsdk.createAddress(w.address, '0'), null);
|
|
1237
|
+
assert.equal(qcsdk.createAddress2(null, salt, initHash), null);
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
test('signing: signSendCoinTransaction and signRawTransaction validate inputs and produce tx data', async () => {
|
|
1241
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded and verifyWallet(newWallet()) must pass');
|
|
1242
|
+
const wallet = qcsdk.newWallet();
|
|
1243
|
+
assert.ok(wallet);
|
|
1244
|
+
|
|
1245
|
+
const signSend = await qcsdk.signSendCoinTransaction(wallet, TO_ADDRESS_EXAMPLE, '0', 0);
|
|
1246
|
+
assert.equal(signSend.resultCode, 0);
|
|
1247
|
+
assert.ok(isHex0x(signSend.txnHash));
|
|
1248
|
+
assert.ok(isHex0x(signSend.txnData));
|
|
1249
|
+
|
|
1250
|
+
const txReq = new qcsdk.TransactionSigningRequest(
|
|
1251
|
+
wallet,
|
|
1252
|
+
TO_ADDRESS_EXAMPLE,
|
|
1253
|
+
'0x0',
|
|
1254
|
+
0,
|
|
1255
|
+
null,
|
|
1256
|
+
21000,
|
|
1257
|
+
null,
|
|
1258
|
+
MAINNET_CHAIN_ID,
|
|
1259
|
+
);
|
|
1260
|
+
const signRaw = qcsdk.signRawTransaction(txReq);
|
|
1261
|
+
assert.equal(signRaw.resultCode, 0);
|
|
1262
|
+
assert.ok(isHex0x(signRaw.txnHash));
|
|
1263
|
+
assert.ok(isHex0x(signRaw.txnData));
|
|
1264
|
+
|
|
1265
|
+
// Negative: invalid to address and invalid remarks length
|
|
1266
|
+
const signSendBad = await qcsdk.signSendCoinTransaction(wallet, 'bad', '1', 0);
|
|
1267
|
+
assert.notEqual(signSendBad.resultCode, 0);
|
|
1268
|
+
|
|
1269
|
+
const longRemarks = '0x' + 'aa'.repeat(33); // > 32 bytes
|
|
1270
|
+
const txReqBad = new qcsdk.TransactionSigningRequest(
|
|
1271
|
+
wallet,
|
|
1272
|
+
TO_ADDRESS_EXAMPLE,
|
|
1273
|
+
'0x0',
|
|
1274
|
+
0,
|
|
1275
|
+
null,
|
|
1276
|
+
21000,
|
|
1277
|
+
longRemarks,
|
|
1278
|
+
MAINNET_CHAIN_ID,
|
|
1279
|
+
);
|
|
1280
|
+
const signRawBad = qcsdk.signRawTransaction(txReqBad);
|
|
1281
|
+
assert.notEqual(signRawBad.resultCode, 0);
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
// ---------------------------------------------------------------------------
|
|
1285
|
+
// Security-fix negative coverage
|
|
1286
|
+
// ---------------------------------------------------------------------------
|
|
1287
|
+
|
|
1288
|
+
test('security: deserializeWallet returns null on malformed JSON (no thrown exception)', () => {
|
|
1289
|
+
const garbage = [
|
|
1290
|
+
'{bad json',
|
|
1291
|
+
'',
|
|
1292
|
+
'null',
|
|
1293
|
+
'42',
|
|
1294
|
+
'{"address":"abc"',
|
|
1295
|
+
undefined,
|
|
1296
|
+
];
|
|
1297
|
+
for (const input of garbage) {
|
|
1298
|
+
const result = qcsdk.deserializeWallet(input);
|
|
1299
|
+
assert.ok(
|
|
1300
|
+
result === null || result === -1000,
|
|
1301
|
+
`deserializeWallet(${JSON.stringify(input)}) should return null or -1000, got ${result}`,
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
test('security: signRawTransaction rejects invalid hex in valueInWei', () => {
|
|
1307
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded');
|
|
1308
|
+
const wallet = qcsdk.newWallet();
|
|
1309
|
+
const txReq = new qcsdk.TransactionSigningRequest(
|
|
1310
|
+
wallet, TO_ADDRESS_EXAMPLE, '0xZZZZ', 0, null, 21000, null, MAINNET_CHAIN_ID,
|
|
1311
|
+
);
|
|
1312
|
+
const res = qcsdk.signRawTransaction(txReq);
|
|
1313
|
+
assert.equal(res.resultCode, -903, 'invalid hex valueInWei must return -903');
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
test('security: signRawTransaction rejects invalid hex in data', () => {
|
|
1317
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded');
|
|
1318
|
+
const wallet = qcsdk.newWallet();
|
|
1319
|
+
const txReq = new qcsdk.TransactionSigningRequest(
|
|
1320
|
+
wallet, TO_ADDRESS_EXAMPLE, '0x0', 0, '0xGGGG', 21000, null, MAINNET_CHAIN_ID,
|
|
1321
|
+
);
|
|
1322
|
+
const res = qcsdk.signRawTransaction(txReq);
|
|
1323
|
+
assert.equal(res.resultCode, -906, 'invalid hex data must return -906');
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1326
|
+
test('security: signRawTransaction rejects invalid hex in remarks', () => {
|
|
1327
|
+
assert.ok(isCirclAvailable(), 'CIRCL WASM must be loaded');
|
|
1328
|
+
const wallet = qcsdk.newWallet();
|
|
1329
|
+
const txReq = new qcsdk.TransactionSigningRequest(
|
|
1330
|
+
wallet, TO_ADDRESS_EXAMPLE, '0x0', 0, null, 21000, '0xZZZZ', MAINNET_CHAIN_ID,
|
|
1331
|
+
);
|
|
1332
|
+
const res = qcsdk.signRawTransaction(txReq);
|
|
1333
|
+
assert.equal(res.resultCode, -909, 'invalid hex remarks must return -909');
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
test('relay read-only APIs: getLatestBlockDetails/getAccountDetails/listAccountTransactions/getTransactionDetails', async () => {
|
|
1337
|
+
const latest = await qcsdk.getLatestBlockDetails();
|
|
1338
|
+
assert.ok(latest);
|
|
1339
|
+
assert.equal(typeof latest.resultCode, 'number');
|
|
1340
|
+
assert.equal(latest.resultCode, 0);
|
|
1341
|
+
assert.ok(latest.blockDetails && typeof latest.blockDetails.blockNumber === 'number');
|
|
1342
|
+
|
|
1343
|
+
const acct = await qcsdk.getAccountDetails(ACCOUNT_ADDRESS_EXAMPLE);
|
|
1344
|
+
assert.ok(acct);
|
|
1345
|
+
assert.equal(typeof acct.resultCode, 'number');
|
|
1346
|
+
// If the relay is healthy, this should be 0 for a valid address.
|
|
1347
|
+
assert.equal(acct.resultCode, 0);
|
|
1348
|
+
assert.ok(acct.accountDetails);
|
|
1349
|
+
assert.equal(acct.accountDetails.address.toLowerCase(), ACCOUNT_ADDRESS_EXAMPLE.toLowerCase());
|
|
1350
|
+
|
|
1351
|
+
const txList = await qcsdk.listAccountTransactions(ACCOUNT_TX_LIST_EXAMPLE, 0);
|
|
1352
|
+
assert.ok(txList);
|
|
1353
|
+
assert.equal(typeof txList.resultCode, 'number');
|
|
1354
|
+
assert.equal(txList.resultCode, 0);
|
|
1355
|
+
assert.ok(txList.listAccountTransactionsResponse);
|
|
1356
|
+
assert.equal(typeof txList.listAccountTransactionsResponse.pageCount, 'number');
|
|
1357
|
+
|
|
1358
|
+
const txd = await qcsdk.getTransactionDetails(TX_HASH_EXAMPLE);
|
|
1359
|
+
assert.ok(txd);
|
|
1360
|
+
assert.equal(typeof txd.resultCode, 'number');
|
|
1361
|
+
// This should normally succeed; if the transaction no longer exists, it may be a 404.
|
|
1362
|
+
if (txd.resultCode === 0) {
|
|
1363
|
+
assert.ok(txd.transactionDetails);
|
|
1364
|
+
assert.ok(isHex0x(txd.transactionDetails.hash));
|
|
1365
|
+
} else {
|
|
1366
|
+
// Negative-but-expected fallback: it must be a "not found" style response.
|
|
1367
|
+
assert.ok(txd.response && typeof txd.response.status === 'number');
|
|
1368
|
+
assert.equal(txd.response.status, 404);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
// Negative: invalid address/hash inputs
|
|
1372
|
+
const badAcct = await qcsdk.getAccountDetails('bad');
|
|
1373
|
+
assert.notEqual(badAcct.resultCode, 0);
|
|
1374
|
+
const badList = await qcsdk.listAccountTransactions('bad', 0);
|
|
1375
|
+
assert.notEqual(badList.resultCode, 0);
|
|
1376
|
+
const badTx = await qcsdk.getTransactionDetails('bad');
|
|
1377
|
+
assert.notEqual(badTx.resultCode, 0);
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1380
|
+
test('transactional API negative validation (no network send): postTransaction/sendCoins', async () => {
|
|
1381
|
+
const postNull = await qcsdk.postTransaction(null);
|
|
1382
|
+
assert.ok(postNull);
|
|
1383
|
+
assert.notEqual(postNull.resultCode, 0);
|
|
1384
|
+
|
|
1385
|
+
const sendBad = await qcsdk.sendCoins(null, TO_ADDRESS_EXAMPLE, '1', 0);
|
|
1386
|
+
assert.notEqual(sendBad.resultCode, 0);
|
|
1387
|
+
});
|
|
1388
|
+
});
|
|
1389
|
+
|