quantumswap 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.github/workflows/publish-npmjs.yaml +22 -0
  2. package/LICENSE +21 -0
  3. package/README.md +287 -0
  4. package/examples/_test-wallet.js +17 -0
  5. package/examples/deploy-IERC20.js +23 -0
  6. package/examples/deploy-QuantumSwapV2ERC20.js +23 -0
  7. package/examples/deploy-QuantumSwapV2Factory.js +23 -0
  8. package/examples/deploy-QuantumSwapV2Pair.js +23 -0
  9. package/examples/deploy-QuantumSwapV2Router02.js +23 -0
  10. package/examples/deploy-WQ.js +23 -0
  11. package/examples/events-IERC20.js +20 -0
  12. package/examples/events-QuantumSwapV2ERC20.js +20 -0
  13. package/examples/events-QuantumSwapV2Factory.js +20 -0
  14. package/examples/events-QuantumSwapV2Pair.js +20 -0
  15. package/examples/events-QuantumSwapV2Router02.js +20 -0
  16. package/examples/events-WQ.js +20 -0
  17. package/examples/offline-signing-IERC20.js +29 -0
  18. package/examples/offline-signing-QuantumSwapV2ERC20.js +29 -0
  19. package/examples/offline-signing-QuantumSwapV2Factory.js +29 -0
  20. package/examples/offline-signing-QuantumSwapV2Pair.js +29 -0
  21. package/examples/offline-signing-QuantumSwapV2Router02.js +29 -0
  22. package/examples/offline-signing-WQ.js +29 -0
  23. package/examples/read-operations-IERC20.js +19 -0
  24. package/examples/read-operations-QuantumSwapV2ERC20.js +19 -0
  25. package/examples/read-operations-QuantumSwapV2Factory.js +19 -0
  26. package/examples/read-operations-QuantumSwapV2Pair.js +19 -0
  27. package/examples/read-operations-QuantumSwapV2Router02.js +19 -0
  28. package/examples/read-operations-WQ.js +19 -0
  29. package/examples/walkthrough-dex-full-flow.js +226 -0
  30. package/examples/walkthrough-dex-full-flow.ts +231 -0
  31. package/examples/write-operations-IERC20.js +22 -0
  32. package/examples/write-operations-QuantumSwapV2ERC20.js +22 -0
  33. package/examples/write-operations-QuantumSwapV2Factory.js +22 -0
  34. package/examples/write-operations-QuantumSwapV2Pair.js +22 -0
  35. package/examples/write-operations-QuantumSwapV2Router02.js +22 -0
  36. package/examples/write-operations-WQ.js +22 -0
  37. package/index.d.ts +1 -0
  38. package/index.js +45 -0
  39. package/package.json +35 -0
  40. package/src/IERC20.d.ts +24 -0
  41. package/src/IERC20.js +348 -0
  42. package/src/IERC20__factory.d.ts +10 -0
  43. package/src/IERC20__factory.js +29 -0
  44. package/src/QuantumSwapV2ERC20.d.ts +24 -0
  45. package/src/QuantumSwapV2ERC20.js +353 -0
  46. package/src/QuantumSwapV2ERC20__factory.d.ts +10 -0
  47. package/src/QuantumSwapV2ERC20__factory.js +29 -0
  48. package/src/QuantumSwapV2Factory.d.ts +24 -0
  49. package/src/QuantumSwapV2Factory.js +310 -0
  50. package/src/QuantumSwapV2Factory__factory.d.ts +10 -0
  51. package/src/QuantumSwapV2Factory__factory.js +29 -0
  52. package/src/QuantumSwapV2Pair.d.ts +44 -0
  53. package/src/QuantumSwapV2Pair.js +847 -0
  54. package/src/QuantumSwapV2Pair__factory.d.ts +10 -0
  55. package/src/QuantumSwapV2Pair__factory.js +29 -0
  56. package/src/QuantumSwapV2Router02.d.ts +47 -0
  57. package/src/QuantumSwapV2Router02.js +1109 -0
  58. package/src/QuantumSwapV2Router02__factory.d.ts +10 -0
  59. package/src/QuantumSwapV2Router02__factory.js +29 -0
  60. package/src/WQ.d.ts +28 -0
  61. package/src/WQ.js +435 -0
  62. package/src/WQ__factory.d.ts +10 -0
  63. package/src/WQ__factory.js +29 -0
  64. package/src/index.d.ts +14 -0
  65. package/src/index.js +15 -0
  66. package/src/quantumcoin-shims.d.ts +25 -0
  67. package/src/types.d.ts +3 -0
  68. package/src/types.js +3 -0
  69. package/test/e2e/IERC20.e2e.test.js +79 -0
  70. package/test/e2e/QuantumSwapV2ERC20.e2e.test.js +79 -0
  71. package/test/e2e/QuantumSwapV2Factory.e2e.test.js +79 -0
  72. package/test/e2e/QuantumSwapV2Pair.e2e.test.js +79 -0
  73. package/test/e2e/QuantumSwapV2Router02.e2e.test.js +79 -0
  74. package/test/e2e/WQ.e2e.test.js +79 -0
  75. package/test/e2e/all-contracts.e2e.test.js +103 -0
  76. package/test/e2e/dex-full-flow.e2e.test.js +353 -0
  77. package/tsconfig.json +14 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @testCategory e2e
3
+ * @blockchainRequired write
4
+ * @description Auto-generated test that deploys and invokes all contracts in this package.
5
+ *
6
+ * WARNING:
7
+ * - This test uses a HARDCODED TEST WALLET (encrypted JSON + passphrase).
8
+ * - It assumes the wallet has funds on the target network.
9
+ * - It will broadcast real transactions and change chain state.
10
+ */
11
+
12
+ const { describe, it } = require("node:test");
13
+ const assert = require("node:assert/strict");
14
+
15
+ const { Initialize } = require("quantumcoin/config");
16
+ const { JsonRpcProvider, Wallet } = require("quantumcoin");
17
+
18
+ const { IERC20, IERC20__factory, QuantumSwapV2ERC20, QuantumSwapV2ERC20__factory, QuantumSwapV2Factory, QuantumSwapV2Factory__factory, QuantumSwapV2Pair, QuantumSwapV2Pair__factory, QuantumSwapV2Router02, QuantumSwapV2Router02__factory, WQ, WQ__factory } = require("../..");
19
+
20
+ // Hardcoded test wallet (test-only; never use for real funds)
21
+ const TEST_WALLET_ENCRYPTED_JSON =
22
+ "{\"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}";
23
+ const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
24
+
25
+ describe("all contracts", () => {
26
+ it("deploys and invokes all contracts", async (t) => {
27
+ const rpcUrl = process.env.QC_RPC_URL;
28
+ if (!rpcUrl) {
29
+ t.skip("QC_RPC_URL not provided");
30
+ return;
31
+ }
32
+
33
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
34
+ await Initialize(null);
35
+
36
+ const provider = new JsonRpcProvider(rpcUrl, chainId);
37
+ const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
38
+
39
+ let deployGasLimit = 600000;
40
+ try {
41
+ const firstFactory = new IERC20__factory(wallet);
42
+ const sampleReq = firstFactory.getDeployTransaction();
43
+ const est = await provider.estimateGas({ from: wallet.address, data: sampleReq.data });
44
+ deployGasLimit = Number(est + 200_000n);
45
+ } catch {
46
+ deployGasLimit = 6_000_000;
47
+ }
48
+
49
+ let IERC20Inst = await (new IERC20__factory(wallet)).deploy({ gasLimit: deployGasLimit });
50
+ const _deployTxIERC20 = IERC20Inst.deployTransaction();
51
+ assert.ok(_deployTxIERC20 && _deployTxIERC20.hash);
52
+ await _deployTxIERC20.wait(1, 600_000);
53
+ const _v = await IERC20Inst.decimals();
54
+ void _v;
55
+ const _tx = await IERC20Inst.approve(wallet.address, 123, { gasLimit: 200000 });
56
+ await _tx.wait(1, 600_000);
57
+
58
+ let QuantumSwapV2ERC20Inst = await (new QuantumSwapV2ERC20__factory(wallet)).deploy({ gasLimit: deployGasLimit });
59
+ const _deployTxQuantumSwapV2ERC20 = QuantumSwapV2ERC20Inst.deployTransaction();
60
+ assert.ok(_deployTxQuantumSwapV2ERC20 && _deployTxQuantumSwapV2ERC20.hash);
61
+ await _deployTxQuantumSwapV2ERC20.wait(1, 600_000);
62
+ const _v = await QuantumSwapV2ERC20Inst.decimals();
63
+ void _v;
64
+ const _tx = await QuantumSwapV2ERC20Inst.approve(wallet.address, 123, { gasLimit: 200000 });
65
+ await _tx.wait(1, 600_000);
66
+
67
+ let QuantumSwapV2FactoryInst = await (new QuantumSwapV2Factory__factory(wallet)).deploy(wallet.address, { gasLimit: deployGasLimit });
68
+ const _deployTxQuantumSwapV2Factory = QuantumSwapV2FactoryInst.deployTransaction();
69
+ assert.ok(_deployTxQuantumSwapV2Factory && _deployTxQuantumSwapV2Factory.hash);
70
+ await _deployTxQuantumSwapV2Factory.wait(1, 600_000);
71
+ const _v = await QuantumSwapV2FactoryInst.INIT_CODE_HASH();
72
+ void _v;
73
+ const _tx = await QuantumSwapV2FactoryInst.createPair(wallet.address, wallet.address, { gasLimit: 200000 });
74
+ await _tx.wait(1, 600_000);
75
+
76
+ let QuantumSwapV2PairInst = await (new QuantumSwapV2Pair__factory(wallet)).deploy({ gasLimit: deployGasLimit });
77
+ const _deployTxQuantumSwapV2Pair = QuantumSwapV2PairInst.deployTransaction();
78
+ assert.ok(_deployTxQuantumSwapV2Pair && _deployTxQuantumSwapV2Pair.hash);
79
+ await _deployTxQuantumSwapV2Pair.wait(1, 600_000);
80
+ const _v = await QuantumSwapV2PairInst.MINIMUM_LIQUIDITY();
81
+ void _v;
82
+ const _tx = await QuantumSwapV2PairInst.approve(wallet.address, 123, { gasLimit: 200000 });
83
+ await _tx.wait(1, 600_000);
84
+
85
+ let QuantumSwapV2Router02Inst = await (new QuantumSwapV2Router02__factory(wallet)).deploy(wallet.address, wallet.address, { gasLimit: deployGasLimit });
86
+ const _deployTxQuantumSwapV2Router02 = QuantumSwapV2Router02Inst.deployTransaction();
87
+ assert.ok(_deployTxQuantumSwapV2Router02 && _deployTxQuantumSwapV2Router02.hash);
88
+ await _deployTxQuantumSwapV2Router02.wait(1, 600_000);
89
+ const _v = await QuantumSwapV2Router02Inst.WETH();
90
+ void _v;
91
+ const _tx = await QuantumSwapV2Router02Inst.addLiquidity(wallet.address, wallet.address, 123, 123, 123, 123, wallet.address, 123, { gasLimit: 200000 });
92
+ await _tx.wait(1, 600_000);
93
+
94
+ let WQInst = await (new WQ__factory(wallet)).deploy({ gasLimit: deployGasLimit });
95
+ const _deployTxWQ = WQInst.deployTransaction();
96
+ assert.ok(_deployTxWQ && _deployTxWQ.hash);
97
+ await _deployTxWQ.wait(1, 600_000);
98
+ const _v = await WQInst.decimals();
99
+ void _v;
100
+ const _tx = await WQInst.approve(wallet.address, 123, { gasLimit: 200000 });
101
+ await _tx.wait(1, 600_000);
102
+ }, { timeout: 1800000});
103
+ });
@@ -0,0 +1,353 @@
1
+ /**
2
+ * @testCategory e2e
3
+ * @blockchainRequired write
4
+ * @description QuantumSwap V2 DEX full flow: deploy WQ/Factory/Router, two ERC20s,
5
+ * create pair, add liquidity (when tokens have supply), quote, token↔token and ETH↔token swaps,
6
+ * balance checks. Uses quantumcoin.js SDK: isAddress, getAddress, parseUnits, formatUnits,
7
+ * Interface.parseLog for event decoding.
8
+ *
9
+ * WARNING: Uses a hardcoded test wallet; broadcasts real transactions.
10
+ */
11
+
12
+ const { describe, it } = require("node:test");
13
+ const assert = require("node:assert/strict");
14
+ const path = require("node:path");
15
+
16
+ const { Initialize } = require("quantumcoin/config");
17
+ const {
18
+ JsonRpcProvider,
19
+ Wallet,
20
+ Contract,
21
+ ContractFactory,
22
+ getCreateAddress,
23
+ isAddress,
24
+ getAddress,
25
+ parseUnits,
26
+ formatUnits,
27
+ Interface,
28
+ } = require("quantumcoin");
29
+
30
+ const {
31
+ WQ,
32
+ WQ__factory,
33
+ QuantumSwapV2Factory,
34
+ QuantumSwapV2Factory__factory,
35
+ QuantumSwapV2Router02,
36
+ QuantumSwapV2Router02__factory,
37
+ QuantumSwapV2Pair,
38
+ IERC20,
39
+ } = require("../..");
40
+
41
+ // SimpleERC20 ABI + bytecode (mint to deployer) — from quantumcoin.js examples
42
+ let SIMPLE_ERC20_ABI;
43
+ let SIMPLE_ERC20_BYTECODE;
44
+ try {
45
+ const qcPkg = require.resolve("quantumcoin/package.json");
46
+ const qcRoot = path.dirname(qcPkg);
47
+ const artifact = require(path.join(qcRoot, "examples", "sdk-generator-erc20.inline.json"));
48
+ const simple = Array.isArray(artifact) ? artifact[0] : artifact;
49
+ SIMPLE_ERC20_ABI = simple.abi;
50
+ SIMPLE_ERC20_BYTECODE = simple.bin;
51
+ } catch (e) {
52
+ SIMPLE_ERC20_ABI = null;
53
+ SIMPLE_ERC20_BYTECODE = null;
54
+ }
55
+
56
+ const TEST_WALLET_ENCRYPTED_JSON =
57
+ "{\"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}";
58
+ const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
59
+
60
+ const DEPLOY_GAS_LIMIT = 6_000_000;
61
+ const TX_GAS_LIMIT = 400_000;
62
+ const DEADLINE_OFFSET = 1200;
63
+
64
+ function deadline() {
65
+ return BigInt(Math.floor(Date.now() / 1000) + DEADLINE_OFFSET);
66
+ }
67
+
68
+ describe("QuantumSwap V2 DEX full flow", () => {
69
+ it("deploys WQ, Factory, Router; validates addresses; deploys two ERC20s; creates pair; parses PairCreated; balance checks", async (t) => {
70
+ const rpcUrl = process.env.QC_RPC_URL;
71
+ if (!rpcUrl) {
72
+ t.skip("QC_RPC_URL not provided");
73
+ return;
74
+ }
75
+ if (!SIMPLE_ERC20_ABI || !SIMPLE_ERC20_BYTECODE) {
76
+ t.skip("SimpleERC20 artifact not found (quantumcoin examples/sdk-generator-erc20.inline.json)");
77
+ return;
78
+ }
79
+
80
+ const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
81
+ await Initialize(null);
82
+
83
+ const provider = new JsonRpcProvider(rpcUrl, chainId);
84
+ const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
85
+
86
+ // --- Address validation (quantumcoin.js isAddress / getAddress) ---
87
+ assert.equal(isAddress(wallet.address), true, "wallet.address must be valid");
88
+ const walletAddr = getAddress(wallet.address);
89
+ assert.ok(walletAddr && walletAddr.length === 66 && walletAddr.startsWith("0x"));
90
+
91
+ // --- 1) Deploy WQ ---
92
+ const wqFactory = new WQ__factory(wallet);
93
+ const wq = await wqFactory.deploy({ gasLimit: DEPLOY_GAS_LIMIT });
94
+ const wqDeployTx = wq.deployTransaction();
95
+ assert.ok(wqDeployTx && wqDeployTx.hash);
96
+ await wqDeployTx.wait(1, 600_000);
97
+ const wqAddress = wq.target;
98
+ assert.ok(isAddress(wqAddress), "WQ address must be valid");
99
+ const wqAddressNorm = getAddress(wqAddress);
100
+
101
+ // --- 2) Deploy QuantumSwapV2Factory ---
102
+ const factoryContract = await new QuantumSwapV2Factory__factory(wallet).deploy(walletAddr, { gasLimit: DEPLOY_GAS_LIMIT });
103
+ const factoryDeployTx = factoryContract.deployTransaction();
104
+ assert.ok(factoryDeployTx && factoryDeployTx.hash);
105
+ await factoryDeployTx.wait(1, 600_000);
106
+ const factoryAddress = factoryContract.target;
107
+ assert.ok(isAddress(factoryAddress), "Factory address must be valid");
108
+ const factoryAddressNorm = getAddress(factoryAddress);
109
+
110
+ // --- 3) Deploy QuantumSwapV2Router02 (needs Factory + WQ) ---
111
+ const routerContract = await new QuantumSwapV2Router02__factory(wallet).deploy(factoryAddressNorm, wqAddressNorm, {
112
+ gasLimit: DEPLOY_GAS_LIMIT,
113
+ });
114
+ const routerDeployTx = routerContract.deployTransaction();
115
+ assert.ok(routerDeployTx && routerDeployTx.hash);
116
+ await routerDeployTx.wait(1, 600_000);
117
+ const routerAddress = routerContract.target;
118
+ assert.ok(isAddress(routerAddress), "Router address must be valid");
119
+
120
+ // Verify Router view calls (ABI / contract interface)
121
+ const routerFactoryFromContract = await routerContract.factory();
122
+ const routerWethFromContract = await routerContract.WETH();
123
+ assert.equal(getAddress(routerFactoryFromContract), factoryAddressNorm, "Router.factory() must match deployed Factory");
124
+ assert.equal(getAddress(routerWethFromContract), wqAddressNorm, "Router.WETH() must match deployed WQ");
125
+
126
+ // --- 4) Deploy two ERC20 tokens (SimpleERC20 with initial supply) ---
127
+ const initialSupply = parseUnits("1000000", 18);
128
+ const simpleErc20Factory = new ContractFactory(SIMPLE_ERC20_ABI, SIMPLE_ERC20_BYTECODE, wallet);
129
+ const deploySimpleErc20 = async (name, symbol) => {
130
+ const tx = simpleErc20Factory.getDeployTransaction(name, symbol, initialSupply);
131
+ const nonce = await provider.getTransactionCount(walletAddr, "pending");
132
+ const address = getCreateAddress({ from: walletAddr, nonce });
133
+ const resp = await wallet.sendTransaction({ ...tx, nonce, gasLimit: DEPLOY_GAS_LIMIT });
134
+ await resp.wait(1, 600_000);
135
+ const contract = new Contract(address, SIMPLE_ERC20_ABI, wallet);
136
+ contract._deployTx = resp;
137
+ return contract;
138
+ };
139
+ const tokenA = await deploySimpleErc20("TokenA", "TKA");
140
+ const tokenAAddress = getAddress(tokenA.target);
141
+ assert.ok(isAddress(tokenAAddress), "TokenA address must be valid");
142
+
143
+ const tokenB = await deploySimpleErc20("TokenB", "TKB");
144
+ const tokenBAddress = getAddress(tokenB.target);
145
+ assert.ok(isAddress(tokenBAddress), "TokenB address must be valid");
146
+
147
+ // --- Balance checks: token A/B (SimpleERC20 mints to deployer) ---
148
+ const tokenABalanceBeforeRaw = await tokenA.balanceOf(walletAddr);
149
+ const tokenBBalanceBeforeRaw = await tokenB.balanceOf(walletAddr);
150
+ const tokenABalanceBefore = typeof tokenABalanceBeforeRaw === "bigint" ? tokenABalanceBeforeRaw : BigInt(String(tokenABalanceBeforeRaw));
151
+ const tokenBBalanceBefore = typeof tokenBBalanceBeforeRaw === "bigint" ? tokenBBalanceBeforeRaw : BigInt(String(tokenBBalanceBeforeRaw));
152
+ assert.ok(tokenABalanceBefore >= initialSupply, "TokenA initial balance >= initialSupply");
153
+ assert.ok(tokenBBalanceBefore >= initialSupply, "TokenB initial balance >= initialSupply");
154
+
155
+ // --- 5) Create pair (tokenA, tokenB); parse PairCreated event when logs present (Interface.parseLog) ---
156
+ const createPairTx = await factoryContract.createPair(tokenAAddress, tokenBAddress, { gasLimit: TX_GAS_LIMIT });
157
+ const createPairReceipt = await createPairTx.wait(1, 600_000);
158
+ assert.ok(createPairReceipt, "createPair receipt must exist");
159
+
160
+ // Pair address from Factory.getPair (ABI call) — source of truth
161
+ const pairFromFactoryRaw = await factoryContract.getPair(tokenAAddress, tokenBAddress);
162
+ let pairAddressFromEvent = null;
163
+ try {
164
+ pairAddressFromEvent = getAddress(pairFromFactoryRaw);
165
+ } catch (e) {
166
+ // Chain may return non-32-byte or invalid format
167
+ pairAddressFromEvent = null;
168
+ }
169
+ const zeroAddress32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
170
+ const pairCreated =
171
+ pairAddressFromEvent &&
172
+ isAddress(pairAddressFromEvent) &&
173
+ pairAddressFromEvent !== zeroAddress32 &&
174
+ pairAddressFromEvent !== "0x" + "0".repeat(64);
175
+
176
+ // When receipt has logs, decode PairCreated using quantumcoin.js Interface.parseLog
177
+ const logs = createPairReceipt.logs;
178
+ if (Array.isArray(logs) && logs.length >= 1) {
179
+ const factoryIface = new Interface(QuantumSwapV2Factory.abi);
180
+ let parsedPairAddress = null;
181
+ for (const log of logs) {
182
+ if (!log.topics || !log.data) continue;
183
+ try {
184
+ const parsed = factoryIface.parseLog({ topics: log.topics, data: log.data });
185
+ if (parsed && parsed.name === "PairCreated") {
186
+ parsedPairAddress = parsed.args.pair ? getAddress(parsed.args.pair) : null;
187
+ assert.ok(parsed.args.token0 && parsed.args.token1, "PairCreated should have token0 and token1");
188
+ break;
189
+ }
190
+ } catch {
191
+ // not this contract's event
192
+ }
193
+ }
194
+ if (parsedPairAddress && pairCreated) {
195
+ assert.equal(parsedPairAddress, pairAddressFromEvent, "PairCreated event pair must match getPair");
196
+ }
197
+ }
198
+
199
+ // --- 6) Attach QuantumSwapV2Pair; reserves (when pair was created) ---
200
+ if (pairCreated) {
201
+ const pairContract = QuantumSwapV2Pair.connect(pairAddressFromEvent, provider);
202
+ const reservesResult = await pairContract.getReserves();
203
+ const reserve0 = Array.isArray(reservesResult) ? reservesResult[0] : reservesResult;
204
+ const reserve1 = Array.isArray(reservesResult) ? reservesResult[1] : 0n;
205
+ assert.ok(typeof reserve0 === "bigint" && typeof reserve1 === "bigint", "getReserves returns bigints");
206
+ assert.equal(reserve0, 0n, "reserve0 before liquidity");
207
+ assert.equal(reserve1, 0n, "reserve1 before liquidity");
208
+ }
209
+
210
+ // --- 7) Quote (getAmountsOut / getAmountsIn) — may revert with no liquidity; use try/catch ---
211
+ const pathTokenAToB = [tokenAAddress, tokenBAddress];
212
+ const amountIn = parseUnits("1", 18);
213
+ let amountsOut = [];
214
+ try {
215
+ amountsOut = await routerContract.getAmountsOut(amountIn, pathTokenAToB);
216
+ } catch (e) {
217
+ // With no liquidity some chains revert
218
+ }
219
+ if (Array.isArray(amountsOut) && amountsOut.length >= 2) {
220
+ const amountOutForOne = amountsOut[1];
221
+ assert.ok(typeof amountOutForOne === "bigint", "amountOut is bigint");
222
+ assert.ok(amountOutForOne === 0n || amountOutForOne > 0n, "amountOut is 0 or positive");
223
+ }
224
+
225
+ let amountsIn = [];
226
+ try {
227
+ const amountOutDesired = parseUnits("1", 18);
228
+ amountsIn = await routerContract.getAmountsIn(amountOutDesired, pathTokenAToB);
229
+ } catch (e) {
230
+ // may revert with no liquidity
231
+ }
232
+ if (Array.isArray(amountsIn) && amountsIn.length >= 1) {
233
+ assert.ok(amountsIn[0] !== undefined, "getAmountsIn[0] defined");
234
+ }
235
+
236
+ // --- 8) Add liquidity (tokenA/tokenB pair) ---
237
+ if (pairCreated) {
238
+ const amountADesired = parseUnits("1000", 18);
239
+ const amountBDesired = parseUnits("1000", 18);
240
+ const amountAMin = 0n;
241
+ const amountBMin = 0n;
242
+ const approveAmount = amountADesired > amountBDesired ? amountADesired : amountBDesired;
243
+ let approveA = await tokenA.approve(routerAddress, approveAmount, { gasLimit: TX_GAS_LIMIT });
244
+ await approveA.wait(1, 600_000);
245
+ let approveB = await tokenB.approve(routerAddress, approveAmount, { gasLimit: TX_GAS_LIMIT });
246
+ await approveB.wait(1, 600_000);
247
+ const addLiqTx = await routerContract.addLiquidity(
248
+ tokenAAddress,
249
+ tokenBAddress,
250
+ amountADesired,
251
+ amountBDesired,
252
+ amountAMin,
253
+ amountBMin,
254
+ walletAddr,
255
+ deadline(),
256
+ { gasLimit: TX_GAS_LIMIT },
257
+ );
258
+ const addLiqReceipt = await addLiqTx.wait(1, 600_000);
259
+ assert.ok(addLiqReceipt && addLiqReceipt.status === 1, "addLiquidity must succeed");
260
+
261
+ const pairContract = QuantumSwapV2Pair.connect(pairAddressFromEvent, provider);
262
+ const reservesAfter = await pairContract.getReserves();
263
+ const res0 = Array.isArray(reservesAfter) ? reservesAfter[0] : reservesAfter;
264
+ const res1 = Array.isArray(reservesAfter) ? reservesAfter[1] : 0n;
265
+ assert.ok(res0 > 0n && res1 > 0n, "reserves after addLiquidity > 0");
266
+ }
267
+
268
+ // --- 9) Swap: swapExactTokensForTokens (tokenA -> tokenB) ---
269
+ if (pairCreated) {
270
+ const swapAmountIn = parseUnits("10", 18);
271
+ const amountOutMin = 0n;
272
+ const pathSwap = [tokenAAddress, tokenBAddress];
273
+ const approveSwap = await tokenA.approve(routerAddress, swapAmountIn, { gasLimit: TX_GAS_LIMIT });
274
+ await approveSwap.wait(1, 600_000);
275
+ const swapTx = await routerContract.swapExactTokensForTokens(
276
+ swapAmountIn,
277
+ amountOutMin,
278
+ pathSwap,
279
+ walletAddr,
280
+ deadline(),
281
+ { gasLimit: TX_GAS_LIMIT },
282
+ );
283
+ const swapReceipt = await swapTx.wait(1, 600_000);
284
+ assert.ok(swapReceipt && swapReceipt.status === 1, "swapExactTokensForTokens must succeed");
285
+ const tokenBBalanceAfterSwapRaw = await tokenB.balanceOf(walletAddr);
286
+ const tokenBBalanceAfterSwap = typeof tokenBBalanceAfterSwapRaw === "bigint" ? tokenBBalanceAfterSwapRaw : BigInt(String(tokenBBalanceAfterSwapRaw));
287
+ assert.ok(tokenBBalanceAfterSwap > tokenBBalanceBefore, "wallet TokenB balance must increase after swap");
288
+ }
289
+
290
+ // --- 10) Swap ETH: wrap, add WQ/token liquidity, swapExactETHForTokens ---
291
+ if (pairCreated) {
292
+ const ethToWrap = parseUnits("1", 18);
293
+ const depositTx = await wq.deposit({ value: ethToWrap, gasLimit: TX_GAS_LIMIT });
294
+ await depositTx.wait(1, 600_000);
295
+ const wqBalanceAfterDepositRaw = await wq.balanceOf(walletAddr);
296
+ const wqBalanceAfterDeposit = typeof wqBalanceAfterDepositRaw === "bigint" ? wqBalanceAfterDepositRaw : BigInt(String(wqBalanceAfterDepositRaw));
297
+ assert.ok(wqBalanceAfterDeposit >= ethToWrap, "WQ balance after deposit >= ethToWrap");
298
+
299
+ const pairWqTokenA = await factoryContract.getPair(wqAddressNorm, tokenAAddress);
300
+ let pairWqTokenAAddr = null;
301
+ try {
302
+ pairWqTokenAAddr = getAddress(pairWqTokenA);
303
+ } catch {
304
+ pairWqTokenAAddr = null;
305
+ }
306
+ const zeroAddr = "0x0000000000000000000000000000000000000000000000000000000000000000";
307
+ const hasWqPair = pairWqTokenAAddr && isAddress(pairWqTokenAAddr) && pairWqTokenAAddr !== zeroAddr && pairWqTokenAAddr !== "0x" + "0".repeat(64);
308
+ if (!hasWqPair) {
309
+ const createWqPairTx = await factoryContract.createPair(wqAddressNorm, tokenAAddress, { gasLimit: TX_GAS_LIMIT });
310
+ await createWqPairTx.wait(1, 600_000);
311
+ pairWqTokenAAddr = getAddress(await factoryContract.getPair(wqAddressNorm, tokenAAddress));
312
+ }
313
+ const tokenForEthLiq = parseUnits("500", 18);
314
+ const approveTokenAForEth = await tokenA.approve(routerAddress, tokenForEthLiq, { gasLimit: TX_GAS_LIMIT });
315
+ await approveTokenAForEth.wait(1, 600_000);
316
+ const addLiqEthTx = await routerContract.addLiquidityETH(
317
+ tokenAAddress,
318
+ tokenForEthLiq,
319
+ 0n,
320
+ 0n,
321
+ walletAddr,
322
+ deadline(),
323
+ { value: ethToWrap, gasLimit: TX_GAS_LIMIT },
324
+ );
325
+ await addLiqEthTx.wait(1, 600_000);
326
+
327
+ const ethSwapValue = parseUnits("0.1", 18);
328
+ const pathEthToToken = [wqAddressNorm, tokenAAddress];
329
+ const swapEthTx = await routerContract.swapExactETHForTokens(
330
+ 0n,
331
+ pathEthToToken,
332
+ walletAddr,
333
+ deadline(),
334
+ { value: ethSwapValue, gasLimit: TX_GAS_LIMIT },
335
+ );
336
+ const swapEthReceipt = await swapEthTx.wait(1, 600_000);
337
+ assert.ok(swapEthReceipt && swapEthReceipt.status === 1, "swapExactETHForTokens must succeed");
338
+ }
339
+
340
+ // --- 11) Account balance check: native balance ---
341
+ const nativeBalanceBefore = await provider.getBalance(walletAddr);
342
+ assert.ok(typeof nativeBalanceBefore === "bigint", "native balance is bigint");
343
+ assert.ok(nativeBalanceBefore >= 0n, "native balance non-negative");
344
+
345
+ const wqBalanceWallet = await wq.balanceOf(walletAddr);
346
+ assert.ok(typeof wqBalanceWallet === "bigint", "WQ balance is bigint");
347
+
348
+ // Summary assertions
349
+ assert.ok(isAddress(routerAddress) && isAddress(factoryAddress) && isAddress(wqAddress), "all core addresses valid");
350
+ assert.ok(isAddress(tokenAAddress) && isAddress(tokenBAddress), "token addresses valid");
351
+ assert.ok(!pairCreated || isAddress(pairAddressFromEvent), "pair address valid when pair created");
352
+ }, { timeout: 300_000 });
353
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "moduleResolution": "node",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "dist",
10
+ "rootDir": "."
11
+ },
12
+ "include": ["examples/**/*.ts", "src/**/*.d.ts", "index.d.ts"],
13
+ "exclude": ["node_modules", "dist"]
14
+ }