quantumcoin 7.0.2 → 7.0.3
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 +2 -4
- package/README.md +2 -4
- package/SPEC.md +2 -4
- package/examples/example-generator-sdk-js.js +1 -1
- package/examples/example-generator-sdk-ts.js +1 -1
- package/examples/offline-signing.js +73 -0
- package/examples/package-lock.json +2 -2
- package/generate-sdk.js +110 -3
- package/package.json +4 -4
- package/src/contract/contract.d.ts +9 -0
- package/src/contract/contract.js +42 -0
- package/src/generator/index.js +216 -13
- package/src/providers/provider.d.ts +7 -0
- package/src/providers/provider.js +12 -0
- package/test/e2e/all-solidity-types.generated-sdks.e2e.test.js +7 -0
- package/test/e2e/simple-erc20.generated-sdks.e2e.test.js +7 -0
- package/test/e2e/transactional.test.js +4 -4
- package/test/e2e/typed-generator.e2e.test.js +2 -0
- package/test/integration/ws-provider.test.js +1 -1
- package/test/unit/generate-contract-cli.test.js +1 -1
- package/test/unit/generator.test.js +1 -0
- package/test/unit/populate-transaction.test.js +62 -0
package/README-SDK.md
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
This is an experimental SDK. Use at your own risk.
|
|
3
|
-
-->
|
|
1
|
+
> **CAUTION:** This is an experimental SDK. Use at your own risk.
|
|
4
2
|
|
|
5
3
|
# QuantumCoin.js — Comprehensive SDK Documentation
|
|
6
4
|
|
|
@@ -671,7 +669,7 @@ It supports generating:
|
|
|
671
669
|
|
|
672
670
|
**Entry point**
|
|
673
671
|
- `node generate-sdk.js ...`
|
|
674
|
-
- or `npx
|
|
672
|
+
- or `npx sdkgen ...` (when installed)
|
|
675
673
|
|
|
676
674
|
### Input modes
|
|
677
675
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
This is an experimental SDK. Use at your own risk.
|
|
3
|
-
-->
|
|
1
|
+
> **CAUTION:** This is an experimental SDK. Use at your own risk.
|
|
4
2
|
|
|
5
3
|
**Comprehensive SDK documentation:** see [`README-SDK.md`](./README-SDK.md).
|
|
6
4
|
|
|
@@ -93,7 +91,7 @@ node generate-sdk.js --abi path/to/abi.json --bin path/to/bytecode.bin
|
|
|
93
91
|
|
|
94
92
|
If installed as a package, you can also run:
|
|
95
93
|
|
|
96
|
-
`npx
|
|
94
|
+
`npx sdkgen --abi ... --bin ...`
|
|
97
95
|
|
|
98
96
|
### Generator typing model
|
|
99
97
|
|
package/SPEC.md
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
This is an experimental SDK. Please do not use for production. Use at own risk
|
|
3
|
-
-->
|
|
1
|
+
> **CAUTION:** This is an experimental SDK. Use at your own risk.
|
|
4
2
|
|
|
5
3
|
# QuantumCoin.js - Ethers.js v6 Compatible API Specification
|
|
6
4
|
|
|
@@ -3823,7 +3821,7 @@ The generator should be invoked as a command-line tool or script:
|
|
|
3823
3821
|
|
|
3824
3822
|
```bash
|
|
3825
3823
|
# As a CLI tool
|
|
3826
|
-
npx
|
|
3824
|
+
npx sdkgen --abi path/to/abi.json --bin path/to/bytecode.bin
|
|
3827
3825
|
|
|
3828
3826
|
# Or as a Node.js script
|
|
3829
3827
|
node generate-sdk.js --abi path/to/abi.json --bin path/to/bytecode.bin
|
|
@@ -70,7 +70,7 @@ function main() {
|
|
|
70
70
|
"--package-name",
|
|
71
71
|
pkgName,
|
|
72
72
|
"--package-description",
|
|
73
|
-
"Example generated package (JS output) for
|
|
73
|
+
"Example generated package (JS output) for sdkgen",
|
|
74
74
|
"--package-author",
|
|
75
75
|
"QuantumCoin Community",
|
|
76
76
|
"--package-license",
|
|
@@ -70,7 +70,7 @@ function main() {
|
|
|
70
70
|
"--package-name",
|
|
71
71
|
pkgName,
|
|
72
72
|
"--package-description",
|
|
73
|
-
"Example generated package (TS output) for
|
|
73
|
+
"Example generated package (TS output) for sdkgen",
|
|
74
74
|
"--package-author",
|
|
75
75
|
"QuantumCoin Community",
|
|
76
76
|
"--package-license",
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Offline signing with populateTransaction + sendRawTransaction.
|
|
3
|
+
*
|
|
4
|
+
* This demonstrates how to:
|
|
5
|
+
* - build unsigned tx data via `contract.populateTransaction.<method>()`
|
|
6
|
+
* - sign it via `wallet.signTransaction(txReq)`
|
|
7
|
+
* - broadcast via `provider.sendRawTransaction(rawTx)`
|
|
8
|
+
*
|
|
9
|
+
* NOTE:
|
|
10
|
+
* - This example WILL broadcast a real transaction and change chain state.
|
|
11
|
+
* - It uses a HARDCODED test wallet (funded) for convenience. Never use for real funds.
|
|
12
|
+
* - Requires `QC_RPC_URL` env var.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const { Initialize } = require("../config");
|
|
16
|
+
const qc = require("..");
|
|
17
|
+
|
|
18
|
+
// Hardcoded test wallet (test-only; never use for real funds)
|
|
19
|
+
const TEST_WALLET_ENCRYPTED_JSON =
|
|
20
|
+
"{\"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}";
|
|
21
|
+
const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
|
|
22
|
+
|
|
23
|
+
async function main() {
|
|
24
|
+
const rpcUrl = process.env.QC_RPC_URL;
|
|
25
|
+
if (!rpcUrl) throw new Error("QC_RPC_URL is required");
|
|
26
|
+
const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
|
|
27
|
+
|
|
28
|
+
await Initialize(null);
|
|
29
|
+
|
|
30
|
+
const provider = new qc.JsonRpcProvider(rpcUrl, chainId);
|
|
31
|
+
const offlineWallet = qc.Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE);
|
|
32
|
+
|
|
33
|
+
// Minimal ABI for demonstration
|
|
34
|
+
const abi = [
|
|
35
|
+
{
|
|
36
|
+
type: "function",
|
|
37
|
+
name: "setValue",
|
|
38
|
+
stateMutability: "nonpayable",
|
|
39
|
+
inputs: [{ name: "value", type: "uint256" }],
|
|
40
|
+
outputs: [],
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const dummyContractAddress = "0x" + "11".repeat(32);
|
|
45
|
+
const contract = new qc.Contract(dummyContractAddress, abi);
|
|
46
|
+
|
|
47
|
+
const txReq = await contract.populateTransaction.setValue(123, { gasLimit: 200000 });
|
|
48
|
+
|
|
49
|
+
// True offline signing: explicitly set nonce + chainId (+ gasPrice if desired)
|
|
50
|
+
const nonce = await provider.getTransactionCount(offlineWallet.address, "pending");
|
|
51
|
+
const rawTx = await offlineWallet.signTransaction({
|
|
52
|
+
...txReq,
|
|
53
|
+
nonce,
|
|
54
|
+
chainId,
|
|
55
|
+
gasPrice: 1n,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
console.log("Unsigned txReq:", txReq);
|
|
59
|
+
console.log("Signed rawTx:", rawTx);
|
|
60
|
+
console.log("Submitting via sendRawTransaction...");
|
|
61
|
+
|
|
62
|
+
const sent = await provider.sendRawTransaction(rawTx);
|
|
63
|
+
console.log("tx hash:", sent.hash);
|
|
64
|
+
const receipt = await sent.wait(1, 600_000);
|
|
65
|
+
console.log("receipt block:", receipt.blockNumber);
|
|
66
|
+
console.log("receipt status:", receipt.status);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
main().catch((e) => {
|
|
70
|
+
console.error(e);
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
});
|
|
73
|
+
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"..": {
|
|
18
18
|
"name": "quantumcoin",
|
|
19
|
-
"version": "7.0.
|
|
19
|
+
"version": "7.0.3",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"quantum-coin-js-sdk": "^1.0.28",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"seed-words": "^1.0.2"
|
|
25
25
|
},
|
|
26
26
|
"bin": {
|
|
27
|
-
"
|
|
27
|
+
"sdkgen": "generate-sdk.js"
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"node_modules/quantum-coin-js-sdk": {
|
package/generate-sdk.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* sdkgen
|
|
4
4
|
*
|
|
5
5
|
* NOTE: This script is the SDK generator CLI entrypoint.
|
|
6
6
|
*
|
|
@@ -20,11 +20,11 @@ const readline = require("node:readline/promises");
|
|
|
20
20
|
const { stdin, stdout } = require("node:process");
|
|
21
21
|
const { execFileSync, spawnSync } = require("node:child_process");
|
|
22
22
|
|
|
23
|
-
const { generate, generateFromArtifacts, generateTransactionalTestJs } = require("./src/generator");
|
|
23
|
+
const { generate, generateFromArtifacts, generateTransactionalTestJs, generateAllContractsTransactionalTestJs } = require("./src/generator");
|
|
24
24
|
|
|
25
25
|
function _helpText() {
|
|
26
26
|
return `
|
|
27
|
-
|
|
27
|
+
sdkgen (generate-sdk.js)
|
|
28
28
|
|
|
29
29
|
Generates TypeScript or JavaScript contract wrappers (plus optional package scaffold, examples and transactional tests).
|
|
30
30
|
|
|
@@ -881,6 +881,13 @@ async function main() {
|
|
|
881
881
|
);
|
|
882
882
|
}
|
|
883
883
|
|
|
884
|
+
if (artifacts.length > 1) {
|
|
885
|
+
_writeText(
|
|
886
|
+
path.join(outDir, "test", "e2e", "all-contracts.e2e.test.js"),
|
|
887
|
+
generateAllContractsTransactionalTestJs({ artifacts: artifacts.map((a) => ({ contractName: a.contractName, abi: a.abi })) }),
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
|
|
884
891
|
if (artifacts.length === 1) {
|
|
885
892
|
// Back-compat: keep original example filenames for a single-contract package.
|
|
886
893
|
const a = artifacts[0];
|
|
@@ -1030,6 +1037,96 @@ async function main() {
|
|
|
1030
1037
|
console.log("No known write method template for this ABI.");
|
|
1031
1038
|
}
|
|
1032
1039
|
|
|
1040
|
+
main().catch((e) => {
|
|
1041
|
+
console.error(e);
|
|
1042
|
+
process.exitCode = 1;
|
|
1043
|
+
});
|
|
1044
|
+
`,
|
|
1045
|
+
);
|
|
1046
|
+
|
|
1047
|
+
_writeText(
|
|
1048
|
+
path.join(outDir, "examples", "offline-signing.js"),
|
|
1049
|
+
`/**
|
|
1050
|
+
* Offline signing example (generated).
|
|
1051
|
+
*
|
|
1052
|
+
* Demonstrates:
|
|
1053
|
+
* - ${a.contractName}__factory.getDeployTransaction(...)
|
|
1054
|
+
* - contract.populateTransaction.<method>(...)
|
|
1055
|
+
* - wallet.signTransaction(txReq) (offline) + provider.sendRawTransaction(rawTx)
|
|
1056
|
+
*
|
|
1057
|
+
* Requires:
|
|
1058
|
+
* - QC_RPC_URL env var
|
|
1059
|
+
*
|
|
1060
|
+
* WARNING: uses a hardcoded test wallet (funded) for convenience.
|
|
1061
|
+
*/
|
|
1062
|
+
const { Initialize } = require("quantumcoin/config");
|
|
1063
|
+
const { JsonRpcProvider, Wallet, getCreateAddress } = require("quantumcoin");
|
|
1064
|
+
const { ${a.contractName}, ${a.contractName}__factory } = require("..");
|
|
1065
|
+
|
|
1066
|
+
// Hardcoded test wallet (test-only; never use for real funds)
|
|
1067
|
+
const TEST_WALLET_ENCRYPTED_JSON =
|
|
1068
|
+
${JSON.stringify(
|
|
1069
|
+
"{\"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}",
|
|
1070
|
+
)};
|
|
1071
|
+
const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
|
|
1072
|
+
|
|
1073
|
+
async function main() {
|
|
1074
|
+
const rpcUrl = process.env.QC_RPC_URL;
|
|
1075
|
+
if (!rpcUrl) throw new Error("QC_RPC_URL is required");
|
|
1076
|
+
const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
|
|
1077
|
+
|
|
1078
|
+
await Initialize(null);
|
|
1079
|
+
const provider = new JsonRpcProvider(rpcUrl, chainId);
|
|
1080
|
+
|
|
1081
|
+
// Offline wallet (no provider attached). We'll resolve nonce from provider manually.
|
|
1082
|
+
const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE);
|
|
1083
|
+
const from = wallet.address;
|
|
1084
|
+
|
|
1085
|
+
const factory = new ${a.contractName}__factory(wallet);
|
|
1086
|
+
const deployTxReq = factory.getDeployTransaction(${ctorArgsExpr});
|
|
1087
|
+
|
|
1088
|
+
let gasLimit = 600000;
|
|
1089
|
+
try {
|
|
1090
|
+
const est = await provider.estimateGas({ from, data: deployTxReq.data });
|
|
1091
|
+
gasLimit = Number(est + 200_000n);
|
|
1092
|
+
} catch {
|
|
1093
|
+
gasLimit = 6_000_000;
|
|
1094
|
+
}
|
|
1095
|
+
const bytecodeSize = (${a.contractName}.bytecode || "").length;
|
|
1096
|
+
if (bytecodeSize > 20000 && gasLimit < 6_000_000) gasLimit = 6_000_000;
|
|
1097
|
+
|
|
1098
|
+
const nonce0 = await provider.getTransactionCount(from, "pending");
|
|
1099
|
+
const predicted = getCreateAddress({ from, nonce: nonce0 });
|
|
1100
|
+
|
|
1101
|
+
const rawDeploy = await wallet.signTransaction({
|
|
1102
|
+
...deployTxReq,
|
|
1103
|
+
nonce: nonce0,
|
|
1104
|
+
chainId,
|
|
1105
|
+
gasLimit,
|
|
1106
|
+
gasPrice: 1n,
|
|
1107
|
+
});
|
|
1108
|
+
|
|
1109
|
+
const sentDeploy = await provider.sendRawTransaction(rawDeploy);
|
|
1110
|
+
console.log("deploy tx hash:", sentDeploy.hash);
|
|
1111
|
+
await sentDeploy.wait(1, 600_000);
|
|
1112
|
+
|
|
1113
|
+
const contract = ${a.contractName}.connect(predicted, provider);
|
|
1114
|
+
console.log("deployed at:", contract.target);
|
|
1115
|
+
|
|
1116
|
+
// Optional: offline-sign a write method if present (ERC20-like approve)
|
|
1117
|
+
if (contract.populateTransaction && typeof contract.populateTransaction.approve === "function") {
|
|
1118
|
+
const txReq = await contract.populateTransaction.approve(from, 123, { gasLimit: 200000 });
|
|
1119
|
+
const nonce1 = await provider.getTransactionCount(from, "pending");
|
|
1120
|
+
const raw = await wallet.signTransaction({ ...txReq, nonce: nonce1, chainId, gasPrice: 1n });
|
|
1121
|
+
const sent = await provider.sendRawTransaction(raw);
|
|
1122
|
+
console.log("approve tx hash:", sent.hash);
|
|
1123
|
+
await sent.wait(1, 600_000);
|
|
1124
|
+
console.log("approve succeeded");
|
|
1125
|
+
} else {
|
|
1126
|
+
console.log("No known write method for offline-signing demo (skipping write tx).");
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1033
1130
|
main().catch((e) => {
|
|
1034
1131
|
console.error(e);
|
|
1035
1132
|
process.exitCode = 1;
|
|
@@ -1098,6 +1195,11 @@ main().catch((e) => {
|
|
|
1098
1195
|
`const { Initialize } = require("quantumcoin/config");\nconst { JsonRpcProvider } = require("quantumcoin");\nconst { createTestWallet } = require("./_test-wallet");\nconst { ${a.contractName} } = require("..");\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL;\n if (!rpcUrl) throw new Error("QC_RPC_URL is required");\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n const address = process.env.CONTRACT_ADDRESS;\n if (!address) throw new Error("CONTRACT_ADDRESS is required");\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const wallet = createTestWallet(provider);\n const contract = ${a.contractName}.connect(address, wallet);\n\n console.log("Connected:", contract.target);\n console.log("Done");\n}\n\nmain().catch((e) => { console.error(e); process.exitCode = 1; });\n`,
|
|
1099
1196
|
);
|
|
1100
1197
|
|
|
1198
|
+
_writeText(
|
|
1199
|
+
path.join(outDir, "examples", `offline-signing-${a.contractName}.js`),
|
|
1200
|
+
`const { Initialize } = require("quantumcoin/config");\nconst { JsonRpcProvider, Wallet, getCreateAddress } = require("quantumcoin");\nconst { TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE } = require("./_test-wallet");\nconst { ${a.contractName}__factory, ${a.contractName} } = require("..");\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL;\n if (!rpcUrl) throw new Error("QC_RPC_URL is required");\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE);\n const from = wallet.address;\n\n const factory = new ${a.contractName}__factory(wallet);\n const deployTxReq = factory.getDeployTransaction();\n const nonce0 = await provider.getTransactionCount(from, \"pending\");\n const predicted = getCreateAddress({ from, nonce: nonce0 });\n\n const rawDeploy = await wallet.signTransaction({ ...deployTxReq, nonce: nonce0, chainId, gasLimit: 6_000_000, gasPrice: 1n });\n const sentDeploy = await provider.sendRawTransaction(rawDeploy);\n await sentDeploy.wait(1, 600_000);\n\n const contract = ${a.contractName}.connect(predicted, provider);\n console.log(\"deployed at:\", contract.target);\n}\n\nmain().catch((e) => { console.error(e); process.exitCode = 1; });\n`,
|
|
1201
|
+
);
|
|
1202
|
+
|
|
1101
1203
|
_writeText(
|
|
1102
1204
|
path.join(outDir, "examples", `events-${a.contractName}.js`),
|
|
1103
1205
|
`const { Initialize } = require("quantumcoin/config");\nconst { JsonRpcProvider } = require("quantumcoin");\nconst { ${a.contractName} } = require("..");\n\nasync function main() {\n const rpcUrl = process.env.QC_RPC_URL;\n if (!rpcUrl) throw new Error("QC_RPC_URL is required");\n const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;\n const address = process.env.CONTRACT_ADDRESS;\n if (!address) throw new Error("CONTRACT_ADDRESS is required");\n await Initialize(null);\n\n const provider = new JsonRpcProvider(rpcUrl, chainId);\n const contract = ${a.contractName}.connect(address, provider);\n\n const logs = await contract.queryFilter("Transfer", "latest", "latest");\n console.log("Logs:", logs.length);\n}\n\nmain().catch((e) => { console.error(e); process.exitCode = 1; });\n`,
|
|
@@ -1117,6 +1219,7 @@ main().catch((e) => {
|
|
|
1117
1219
|
}
|
|
1118
1220
|
}
|
|
1119
1221
|
|
|
1222
|
+
console.warn("This is an experimental SDK. Use at your own risk.");
|
|
1120
1223
|
console.log(`Generated contract files in: ${targetSrcDir}`);
|
|
1121
1224
|
}
|
|
1122
1225
|
|
|
@@ -1249,6 +1352,7 @@ function _packageReadme({ pkgName, pkgDesc, artifacts, createdFromSolidity, lang
|
|
|
1249
1352
|
return (
|
|
1250
1353
|
`# ${pkgName}\n\n` +
|
|
1251
1354
|
`${pkgDesc || ""}\n\n` +
|
|
1355
|
+
"> **Note:** This is an experimental SDK. Use at your own risk.\n\n" +
|
|
1252
1356
|
"## What’s in this package\n\n" +
|
|
1253
1357
|
(outLang === "ts"
|
|
1254
1358
|
? "- Typed contract wrappers and factories in `src/` (compiled output in `dist/`)\n"
|
|
@@ -1324,6 +1428,7 @@ function _createPackageScaffold({ outDir, pkgName, pkgDesc, pkgAuthor, pkgLicens
|
|
|
1324
1428
|
_writeJson(path.join(outDir, "tsconfig.json"), {
|
|
1325
1429
|
compilerOptions: {
|
|
1326
1430
|
target: "ES2022",
|
|
1431
|
+
lib: ["ES2022"],
|
|
1327
1432
|
module: "CommonJS",
|
|
1328
1433
|
declaration: true,
|
|
1329
1434
|
outDir: "dist",
|
|
@@ -1360,6 +1465,8 @@ function _createPackageScaffold({ outDir, pkgName, pkgDesc, pkgAuthor, pkgLicens
|
|
|
1360
1465
|
` constructor(address: string, abi: any, runner?: any, bytecode?: any);\n` +
|
|
1361
1466
|
` target: string;\n` +
|
|
1362
1467
|
` address: string;\n` +
|
|
1468
|
+
` interface: any;\n` +
|
|
1469
|
+
` populateTransaction: any;\n` +
|
|
1363
1470
|
` call(methodName: string, args: any[], overrides?: TransactionRequest): Promise<any>;\n` +
|
|
1364
1471
|
` send(methodName: string, args: any[], overrides?: TransactionRequest): Promise<ContractTransactionResponse>;\n` +
|
|
1365
1472
|
` deployTransaction(): TransactionResponse | null;\n` +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quantumcoin",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.3",
|
|
4
4
|
"description": "QuantumCoin SDK - a complete SDK for dapps",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
"./package.json": "./package.json"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
|
-
"test": "node --test \"test/**/*.test.js\"",
|
|
33
|
+
"test": "node --test \"test/unit/**/*.test.js\" \"test/integration/**/*.test.js\" \"test/security/**/*.test.js\" \"test/e2e/*.test.js\"",
|
|
34
34
|
"test:unit": "node --test \"test/unit/**/*.test.js\"",
|
|
35
35
|
"test:integration": "node --test \"test/integration/**/*.test.js\"",
|
|
36
36
|
"test:security": "node --test \"test/security/**/*.test.js\"",
|
|
37
37
|
"test:non-transactional": "node --test \"test/unit/**/*.test.js\" \"test/integration/**/*.test.js\" \"test/security/**/*.test.js\"",
|
|
38
|
-
"test:e2e": "node --test --test-concurrency=1 \"test/e2e
|
|
38
|
+
"test:e2e": "node --test --test-concurrency=1 \"test/e2e/*.test.js\"",
|
|
39
39
|
"example": "node examples/example.js",
|
|
40
40
|
"example:provider": "node examples/example.js",
|
|
41
41
|
"example:wallet": "node examples/wallet-offline.js",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
"homepage": "https://github.com/quantumcoinproject/quantumcoin.js#readme",
|
|
67
67
|
"bin": {
|
|
68
|
-
"
|
|
68
|
+
"sdkgen": "./generate-sdk.js"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
71
|
"quantum-coin-pqc-js-sdk": "^1.0.5",
|
|
@@ -20,6 +20,9 @@ export class Contract extends BaseContract {
|
|
|
20
20
|
signer: any;
|
|
21
21
|
runner: any;
|
|
22
22
|
_listeners: any;
|
|
23
|
+
populateTransaction: {
|
|
24
|
+
[key: string]: (...args: any[]) => Promise<import("../providers/provider").TransactionRequest>;
|
|
25
|
+
};
|
|
23
26
|
getAddress(): string;
|
|
24
27
|
/**
|
|
25
28
|
* Invoke a contract function, dispatching to call() or send().
|
|
@@ -44,6 +47,12 @@ export class Contract extends BaseContract {
|
|
|
44
47
|
* @returns {Promise<ContractTransactionResponse>}
|
|
45
48
|
*/
|
|
46
49
|
send(methodName: string, args: any[], overrides?: import("../providers/provider").TransactionRequest | undefined): Promise<ContractTransactionResponse>;
|
|
50
|
+
/**
|
|
51
|
+
* Build an unsigned transaction request for a contract method call.
|
|
52
|
+
* @param {string} methodName
|
|
53
|
+
* @param {any[]} args
|
|
54
|
+
*/
|
|
55
|
+
_populate(methodName: string, args: any[]): Promise<import("../providers/provider").TransactionRequest>;
|
|
47
56
|
/**
|
|
48
57
|
* Query logs for an event.
|
|
49
58
|
* @param {any} event
|
package/src/contract/contract.js
CHANGED
|
@@ -132,6 +132,25 @@ class Contract extends BaseContract {
|
|
|
132
132
|
|
|
133
133
|
this._listeners = new Map();
|
|
134
134
|
|
|
135
|
+
// ethers-style populateTransaction namespace:
|
|
136
|
+
// await contract.populateTransaction.someMethod(arg1, ..., overrides?)
|
|
137
|
+
//
|
|
138
|
+
// NOTE: This will shadow any ABI function literally named "populateTransaction".
|
|
139
|
+
// Such a function can still be invoked via `contract.call("populateTransaction", ...)`
|
|
140
|
+
// or `contract.send("populateTransaction", ...)`.
|
|
141
|
+
const self = this;
|
|
142
|
+
this.populateTransaction = new Proxy(
|
|
143
|
+
{},
|
|
144
|
+
{
|
|
145
|
+
get(_t, prop) {
|
|
146
|
+
if (typeof prop !== "string") return undefined;
|
|
147
|
+
const fn = self.interface.abi.find((f) => f && f.type === "function" && f.name === prop);
|
|
148
|
+
if (!fn) return undefined;
|
|
149
|
+
return (...args) => self._populate(prop, args);
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
|
|
135
154
|
// Proxy to support dynamic method names: contract.someMethod(...)
|
|
136
155
|
return new Proxy(this, {
|
|
137
156
|
get: (target, prop, receiver) => {
|
|
@@ -183,6 +202,29 @@ class Contract extends BaseContract {
|
|
|
183
202
|
return this.send(methodName, callArgs, overrides);
|
|
184
203
|
}
|
|
185
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Build an unsigned transaction request for a contract method call.
|
|
207
|
+
* @param {string} methodName
|
|
208
|
+
* @param {any[]} args
|
|
209
|
+
* @returns {Promise<import("../providers/provider").TransactionRequest>}
|
|
210
|
+
*/
|
|
211
|
+
async _populate(methodName, args) {
|
|
212
|
+
const fn = this.interface.abi.find((f) => f && f.type === "function" && f.name === methodName);
|
|
213
|
+
if (!fn) throw makeError("function not found", "INVALID_ARGUMENT", { methodName });
|
|
214
|
+
|
|
215
|
+
const inputCount = Array.isArray(fn.inputs) ? fn.inputs.length : 0;
|
|
216
|
+
let overrides = undefined;
|
|
217
|
+
let callArgs = Array.isArray(args) ? args : [];
|
|
218
|
+
|
|
219
|
+
if (callArgs.length === inputCount + 1 && _isOverridesLike(callArgs[callArgs.length - 1])) {
|
|
220
|
+
overrides = callArgs[callArgs.length - 1];
|
|
221
|
+
callArgs = callArgs.slice(0, inputCount);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const data = this.interface.encodeFunctionData(methodName, callArgs);
|
|
225
|
+
return { to: this.address, data, ...(overrides || {}) };
|
|
226
|
+
}
|
|
227
|
+
|
|
186
228
|
/**
|
|
187
229
|
* Perform a read-only call.
|
|
188
230
|
* @param {string} methodName
|
package/src/generator/index.js
CHANGED
|
@@ -455,19 +455,19 @@ function _allSupportedParams(inputs) {
|
|
|
455
455
|
|
|
456
456
|
function _typesTs() {
|
|
457
457
|
return (
|
|
458
|
-
`// Auto-generated by
|
|
458
|
+
`// Auto-generated by sdkgen\n\n` +
|
|
459
459
|
`// Re-export ALL Solidity-related types from quantumcoin.\n` +
|
|
460
460
|
`export type * from "quantumcoin/types";\n`
|
|
461
461
|
);
|
|
462
462
|
}
|
|
463
463
|
|
|
464
464
|
function _typesJs() {
|
|
465
|
-
return `// Auto-generated by
|
|
465
|
+
return `// Auto-generated by sdkgen\n\n` + `module.exports = {};\n`;
|
|
466
466
|
}
|
|
467
467
|
|
|
468
468
|
function _typesDts() {
|
|
469
469
|
return (
|
|
470
|
-
`// Auto-generated by
|
|
470
|
+
`// Auto-generated by sdkgen\n\n` +
|
|
471
471
|
`export type * from "quantumcoin/types";\n`
|
|
472
472
|
);
|
|
473
473
|
}
|
|
@@ -475,9 +475,10 @@ function _typesDts() {
|
|
|
475
475
|
function _renderContractTs({ contractName, abi, bytecode, docs }) {
|
|
476
476
|
const functions = (abi || []).filter((f) => f && f.type === "function");
|
|
477
477
|
const tupleReg = _collectTupleRegistry(contractName, abi);
|
|
478
|
+
const txFns = functions.filter((f) => (f.stateMutability || "") !== "view" && (f.stateMutability || "") !== "pure");
|
|
478
479
|
|
|
479
480
|
const contractTsLines = [];
|
|
480
|
-
contractTsLines.push(`// Auto-generated by
|
|
481
|
+
contractTsLines.push(`// Auto-generated by sdkgen`);
|
|
481
482
|
contractTsLines.push(`import { Contract, ContractTransactionResponse, ContractRunner, TransactionResponse } from "quantumcoin";`);
|
|
482
483
|
contractTsLines.push(`import type * as Types from "./types";`);
|
|
483
484
|
contractTsLines.push(``);
|
|
@@ -506,6 +507,26 @@ function _renderContractTs({ contractName, abi, bytecode, docs }) {
|
|
|
506
507
|
contractTsLines.push(` super(address, ${contractName}.abi as any, runner as any, ${contractName}.bytecode);`);
|
|
507
508
|
contractTsLines.push(` // @ts-expect-error internal attach`);
|
|
508
509
|
contractTsLines.push(` this._deployTx = _deployTx;`);
|
|
510
|
+
if (txFns.length) {
|
|
511
|
+
contractTsLines.push(``);
|
|
512
|
+
contractTsLines.push(` // Typed populateTransaction helpers (offline signing / sendRawTransaction flows)`);
|
|
513
|
+
contractTsLines.push(` this.populateTransaction = {`);
|
|
514
|
+
for (const fn of txFns) {
|
|
515
|
+
const name = fn.name;
|
|
516
|
+
const inputs = fn.inputs || [];
|
|
517
|
+
const argsSig = inputs
|
|
518
|
+
.map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`)
|
|
519
|
+
.join(", ");
|
|
520
|
+
const argsNames = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
|
|
521
|
+
contractTsLines.push(
|
|
522
|
+
` ${name}: async (${argsSig}${argsSig ? ", " : ""}overrides?: any): Promise<import("quantumcoin").TransactionRequest> => {`,
|
|
523
|
+
);
|
|
524
|
+
contractTsLines.push(` const data = this.interface.encodeFunctionData(${JSON.stringify(name)}, [${argsNames}]);`);
|
|
525
|
+
contractTsLines.push(` return { to: this.address, data, ...(overrides || {}) };`);
|
|
526
|
+
contractTsLines.push(` },`);
|
|
527
|
+
}
|
|
528
|
+
contractTsLines.push(` } as any;`);
|
|
529
|
+
}
|
|
509
530
|
contractTsLines.push(` }`);
|
|
510
531
|
|
|
511
532
|
for (const fn of functions) {
|
|
@@ -570,9 +591,10 @@ function _renderContractTs({ contractName, abi, bytecode, docs }) {
|
|
|
570
591
|
function _renderContractJs({ contractName, abi, bytecode, docs }) {
|
|
571
592
|
const functions = (abi || []).filter((f) => f && f.type === "function");
|
|
572
593
|
const tupleReg = _collectTupleRegistry(contractName, abi);
|
|
594
|
+
const txFns = functions.filter((f) => (f.stateMutability || "") !== "view" && (f.stateMutability || "") !== "pure");
|
|
573
595
|
|
|
574
596
|
const lines = [];
|
|
575
|
-
lines.push(`// Auto-generated by
|
|
597
|
+
lines.push(`// Auto-generated by sdkgen`);
|
|
576
598
|
lines.push(`const { Contract } = require("quantumcoin");`);
|
|
577
599
|
lines.push("");
|
|
578
600
|
|
|
@@ -604,6 +626,22 @@ function _renderContractJs({ contractName, abi, bytecode, docs }) {
|
|
|
604
626
|
lines.push(` constructor(address, runner, _deployTx) {`);
|
|
605
627
|
lines.push(` super(address, ${contractName}.abi, runner, ${contractName}.bytecode);`);
|
|
606
628
|
lines.push(` this._deployTx = _deployTx;`);
|
|
629
|
+
if (txFns.length) {
|
|
630
|
+
lines.push("");
|
|
631
|
+
lines.push(" // Typed populateTransaction helpers (offline signing / sendRawTransaction flows)");
|
|
632
|
+
lines.push(" this.populateTransaction = {");
|
|
633
|
+
for (const fn of txFns) {
|
|
634
|
+
const name = fn.name;
|
|
635
|
+
const inputs = fn.inputs || [];
|
|
636
|
+
const argsNames = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
|
|
637
|
+
const argsSig = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
|
|
638
|
+
lines.push(` ${name}: async (${argsSig}${argsSig ? ", " : ""}overrides) => {`);
|
|
639
|
+
lines.push(` const data = this.interface.encodeFunctionData(${JSON.stringify(name)}, [${argsNames}]);`);
|
|
640
|
+
lines.push(` return { to: this.address, data, ...(overrides || {}) };`);
|
|
641
|
+
lines.push(" },");
|
|
642
|
+
}
|
|
643
|
+
lines.push(" };");
|
|
644
|
+
}
|
|
607
645
|
lines.push(` }`);
|
|
608
646
|
|
|
609
647
|
for (const fn of functions) {
|
|
@@ -674,8 +712,9 @@ function _renderContractJs({ contractName, abi, bytecode, docs }) {
|
|
|
674
712
|
function _renderContractDts({ contractName, abi }) {
|
|
675
713
|
const functions = (abi || []).filter((f) => f && f.type === "function");
|
|
676
714
|
const tupleReg = _collectTupleRegistry(contractName, abi);
|
|
715
|
+
const txFns = functions.filter((f) => (f.stateMutability || "") !== "view" && (f.stateMutability || "") !== "pure");
|
|
677
716
|
const lines = [];
|
|
678
|
-
lines.push(`// Auto-generated by
|
|
717
|
+
lines.push(`// Auto-generated by sdkgen`);
|
|
679
718
|
lines.push(`import { Contract, ContractRunner, ContractTransactionResponse, TransactionResponse } from "quantumcoin";`);
|
|
680
719
|
lines.push(`import type * as Types from "./types";`);
|
|
681
720
|
lines.push("");
|
|
@@ -689,6 +728,20 @@ function _renderContractDts({ contractName, abi }) {
|
|
|
689
728
|
lines.push(` static readonly bytecode: string;`);
|
|
690
729
|
lines.push(` static connect(address: string, runner?: ContractRunner): ${contractName};`);
|
|
691
730
|
lines.push(` constructor(address: string, runner?: ContractRunner, _deployTx?: TransactionResponse);`);
|
|
731
|
+
if (txFns.length) {
|
|
732
|
+
lines.push(` readonly populateTransaction: {`);
|
|
733
|
+
for (const fn of txFns) {
|
|
734
|
+
const name = fn.name;
|
|
735
|
+
const inputs = fn.inputs || [];
|
|
736
|
+
const argsSig = inputs
|
|
737
|
+
.map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`)
|
|
738
|
+
.join(", ");
|
|
739
|
+
lines.push(
|
|
740
|
+
` ${name}(${argsSig}${argsSig ? ", " : ""}overrides?: any): Promise<import("quantumcoin").TransactionRequest>;`,
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
lines.push(` };`);
|
|
744
|
+
}
|
|
692
745
|
|
|
693
746
|
for (const fn of functions) {
|
|
694
747
|
const name = fn.name;
|
|
@@ -727,7 +780,7 @@ function _renderFactoryTs({ contractName, abi }) {
|
|
|
727
780
|
const tupleReg = _collectTupleRegistry(contractName, abi);
|
|
728
781
|
|
|
729
782
|
const factoryTsLines = [];
|
|
730
|
-
factoryTsLines.push(`// Auto-generated by
|
|
783
|
+
factoryTsLines.push(`// Auto-generated by sdkgen`);
|
|
731
784
|
factoryTsLines.push(`import { ContractFactory, ContractRunner, getCreateAddress } from "quantumcoin";`);
|
|
732
785
|
factoryTsLines.push(`import { ${contractName} } from "./${contractName}";`);
|
|
733
786
|
factoryTsLines.push(`import type * as Types from "./types";`);
|
|
@@ -793,7 +846,7 @@ function _renderFactoryJs({ contractName, abi }) {
|
|
|
793
846
|
const deployArgsNames = ctorInputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
|
|
794
847
|
|
|
795
848
|
const lines = [];
|
|
796
|
-
lines.push(`// Auto-generated by
|
|
849
|
+
lines.push(`// Auto-generated by sdkgen`);
|
|
797
850
|
lines.push(`const { ContractFactory, getCreateAddress } = require("quantumcoin");`);
|
|
798
851
|
lines.push(`const { ${contractName} } = require("./${contractName}");`);
|
|
799
852
|
lines.push("");
|
|
@@ -834,7 +887,7 @@ function _renderFactoryDts({ contractName, abi }) {
|
|
|
834
887
|
const deployArgsSig = ctorInputs.map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`).join(", ");
|
|
835
888
|
|
|
836
889
|
const lines = [];
|
|
837
|
-
lines.push(`// Auto-generated by
|
|
890
|
+
lines.push(`// Auto-generated by sdkgen`);
|
|
838
891
|
lines.push(`import { ContractFactory, ContractRunner } from "quantumcoin";`);
|
|
839
892
|
lines.push(`import { ${contractName} } from "./${contractName}";`);
|
|
840
893
|
lines.push(`import type * as Types from "./types";`);
|
|
@@ -867,7 +920,7 @@ function _renderFactoryDts({ contractName, abi }) {
|
|
|
867
920
|
|
|
868
921
|
function _renderIndexTs(contractNames) {
|
|
869
922
|
const lines = [];
|
|
870
|
-
lines.push(`// Auto-generated by
|
|
923
|
+
lines.push(`// Auto-generated by sdkgen`);
|
|
871
924
|
lines.push(`export * from "./types";`);
|
|
872
925
|
for (const name of contractNames) {
|
|
873
926
|
lines.push(`export * from "./${name}";`);
|
|
@@ -878,7 +931,7 @@ function _renderIndexTs(contractNames) {
|
|
|
878
931
|
|
|
879
932
|
function _renderIndexJs(contractNames) {
|
|
880
933
|
const lines = [];
|
|
881
|
-
lines.push(`// Auto-generated by
|
|
934
|
+
lines.push(`// Auto-generated by sdkgen`);
|
|
882
935
|
lines.push("");
|
|
883
936
|
lines.push(`Object.assign(exports, require("./types"));`);
|
|
884
937
|
for (const name of contractNames) {
|
|
@@ -891,7 +944,7 @@ function _renderIndexJs(contractNames) {
|
|
|
891
944
|
|
|
892
945
|
function _renderIndexDts(contractNames) {
|
|
893
946
|
const lines = [];
|
|
894
|
-
lines.push(`// Auto-generated by
|
|
947
|
+
lines.push(`// Auto-generated by sdkgen`);
|
|
895
948
|
lines.push(`export * from "./types";`);
|
|
896
949
|
for (const name of contractNames) {
|
|
897
950
|
lines.push(`export * from "./${name}";`);
|
|
@@ -1095,6 +1148,156 @@ describe("${contractName} transactional", () => {
|
|
|
1095
1148
|
`;
|
|
1096
1149
|
}
|
|
1097
1150
|
|
|
1151
|
+
/**
|
|
1152
|
+
* For a single contract ABI, compute deploy args and optional view/write method for e2e.
|
|
1153
|
+
* @param {{ contractName: string, abi: any[] }} opts
|
|
1154
|
+
* @returns {{ ctorArgsExpr: string, deployArgsExpr: string, viewName: string | null, writeName: string | null, writeArgsExpr: string }}
|
|
1155
|
+
*/
|
|
1156
|
+
function _getContractTestMeta(opts) {
|
|
1157
|
+
const { contractName, abi } = opts;
|
|
1158
|
+
const ctor = _findConstructor(abi);
|
|
1159
|
+
const ctorInputs = ctor.inputs || [];
|
|
1160
|
+
const ctorArgsExpr = ctorInputs.map((i) => _solTypeToTestValueExpr(i)).join(", ");
|
|
1161
|
+
const viewNoArg = (abi || []).find(
|
|
1162
|
+
(f) => f && f.type === "function" && (f.stateMutability === "view" || f.stateMutability === "pure") && (f.inputs || []).length === 0,
|
|
1163
|
+
);
|
|
1164
|
+
const writeFn =
|
|
1165
|
+
(abi || []).find(
|
|
1166
|
+
(f) =>
|
|
1167
|
+
f && f.type === "function" && !(f.stateMutability === "view" || f.stateMutability === "pure") && f.name === "set" &&
|
|
1168
|
+
(f.inputs || []).length === 1 &&
|
|
1169
|
+
(f.inputs[0].type || "").startsWith("uint") &&
|
|
1170
|
+
_allSupportedParams(f.inputs),
|
|
1171
|
+
) ||
|
|
1172
|
+
(abi || []).find(
|
|
1173
|
+
(f) => f && f.type === "function" && !(f.stateMutability === "view" || f.stateMutability === "pure") && _allSupportedParams(f.inputs),
|
|
1174
|
+
);
|
|
1175
|
+
const writeArgsExpr =
|
|
1176
|
+
writeFn && (writeFn.inputs || []).length === 1 && (writeFn.inputs[0].type || "").startsWith("uint")
|
|
1177
|
+
? "456"
|
|
1178
|
+
: writeFn
|
|
1179
|
+
? (writeFn.inputs || []).map((i) => _solTypeToTestValueExpr(i)).join(", ")
|
|
1180
|
+
: "";
|
|
1181
|
+
return {
|
|
1182
|
+
ctorArgsExpr,
|
|
1183
|
+
deployArgsExpr: ctorArgsExpr,
|
|
1184
|
+
viewName: viewNoArg ? viewNoArg.name : null,
|
|
1185
|
+
writeName: writeFn ? writeFn.name : null,
|
|
1186
|
+
writeArgsExpr,
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* Generate a single transactional e2e test that deploys and invokes methods on ALL contracts.
|
|
1192
|
+
* Used when the package has multiple contracts so one test exercises every contract.
|
|
1193
|
+
*
|
|
1194
|
+
* @param {{ artifacts: Array<{ contractName: string, abi: any[] }> }} opts
|
|
1195
|
+
* @returns {string}
|
|
1196
|
+
*/
|
|
1197
|
+
function generateAllContractsTransactionalTestJs(opts) {
|
|
1198
|
+
const { artifacts } = opts;
|
|
1199
|
+
if (!artifacts || artifacts.length < 2) {
|
|
1200
|
+
throw new Error("generateAllContractsTransactionalTestJs requires at least 2 artifacts");
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
const requireNames = [];
|
|
1204
|
+
for (const a of artifacts) {
|
|
1205
|
+
requireNames.push(a.contractName, `${a.contractName}__factory`);
|
|
1206
|
+
}
|
|
1207
|
+
const requireLine = `const { ${requireNames.join(", ")} } = require("../..");`;
|
|
1208
|
+
|
|
1209
|
+
const blocks = [];
|
|
1210
|
+
for (const a of artifacts) {
|
|
1211
|
+
const meta = _getContractTestMeta({ contractName: a.contractName, abi: a.abi });
|
|
1212
|
+
const factoryName = `${a.contractName}__factory`;
|
|
1213
|
+
const deployRhs =
|
|
1214
|
+
meta.ctorArgsExpr
|
|
1215
|
+
? `await (new ${factoryName}(wallet)).deploy(${meta.deployArgsExpr}, { gasLimit: deployGasLimit })`
|
|
1216
|
+
: `await (new ${factoryName}(wallet)).deploy({ gasLimit: deployGasLimit })`;
|
|
1217
|
+
let invokeBlock = "";
|
|
1218
|
+
if (meta.viewName) {
|
|
1219
|
+
invokeBlock += `const _v = await ${a.contractName}Inst.${meta.viewName}();\n void _v;\n`;
|
|
1220
|
+
}
|
|
1221
|
+
if (meta.writeName) {
|
|
1222
|
+
invokeBlock += `const _tx = await ${a.contractName}Inst.${meta.writeName}(${meta.writeArgsExpr}${meta.writeArgsExpr ? ", " : ""}{ gasLimit: 200000 });\n await _tx.wait(1, 600_000);\n`;
|
|
1223
|
+
}
|
|
1224
|
+
blocks.push({
|
|
1225
|
+
contractName: a.contractName,
|
|
1226
|
+
factoryName,
|
|
1227
|
+
deployRhs,
|
|
1228
|
+
invokeBlock: invokeBlock || `// No view/write method detected for ${a.contractName}\n`,
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
const deployAndInvokeBlocks = blocks
|
|
1233
|
+
.map(
|
|
1234
|
+
(b) => `
|
|
1235
|
+
let ${b.contractName}Inst = ${b.deployRhs};
|
|
1236
|
+
const _deployTx${b.contractName} = ${b.contractName}Inst.deployTransaction();
|
|
1237
|
+
assert.ok(_deployTx${b.contractName} && _deployTx${b.contractName}.hash);
|
|
1238
|
+
await _deployTx${b.contractName}.wait(1, 600_000);
|
|
1239
|
+
${b.invokeBlock.trim().split("\n").join("\n ")}`,
|
|
1240
|
+
)
|
|
1241
|
+
.join("\n");
|
|
1242
|
+
|
|
1243
|
+
const firstMeta = _getContractTestMeta({ contractName: artifacts[0].contractName, abi: artifacts[0].abi });
|
|
1244
|
+
const firstCtorArgs = firstMeta.ctorArgsExpr ? firstMeta.ctorArgsExpr + ", " : "";
|
|
1245
|
+
|
|
1246
|
+
return `/**
|
|
1247
|
+
* @testCategory e2e
|
|
1248
|
+
* @blockchainRequired write
|
|
1249
|
+
* @description Auto-generated test that deploys and invokes all contracts in this package.
|
|
1250
|
+
*
|
|
1251
|
+
* WARNING:
|
|
1252
|
+
* - This test uses a HARDCODED TEST WALLET (encrypted JSON + passphrase).
|
|
1253
|
+
* - It assumes the wallet has funds on the target network.
|
|
1254
|
+
* - It will broadcast real transactions and change chain state.
|
|
1255
|
+
*/
|
|
1256
|
+
|
|
1257
|
+
const { describe, it } = require("node:test");
|
|
1258
|
+
const assert = require("node:assert/strict");
|
|
1259
|
+
|
|
1260
|
+
const { Initialize } = require("quantumcoin/config");
|
|
1261
|
+
const { JsonRpcProvider, Wallet } = require("quantumcoin");
|
|
1262
|
+
|
|
1263
|
+
${requireLine}
|
|
1264
|
+
|
|
1265
|
+
// Hardcoded test wallet (test-only; never use for real funds)
|
|
1266
|
+
const TEST_WALLET_ENCRYPTED_JSON =
|
|
1267
|
+
${JSON.stringify(
|
|
1268
|
+
"{\"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}",
|
|
1269
|
+
)};
|
|
1270
|
+
const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
|
|
1271
|
+
|
|
1272
|
+
describe("all contracts", () => {
|
|
1273
|
+
it("deploys and invokes all contracts", async (t) => {
|
|
1274
|
+
const rpcUrl = process.env.QC_RPC_URL;
|
|
1275
|
+
if (!rpcUrl) {
|
|
1276
|
+
t.skip("QC_RPC_URL not provided");
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
|
|
1281
|
+
await Initialize(null);
|
|
1282
|
+
|
|
1283
|
+
const provider = new JsonRpcProvider(rpcUrl, chainId);
|
|
1284
|
+
const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
|
|
1285
|
+
|
|
1286
|
+
let deployGasLimit = 600000;
|
|
1287
|
+
try {
|
|
1288
|
+
const firstFactory = new ${artifacts[0].contractName}__factory(wallet);
|
|
1289
|
+
const sampleReq = firstFactory.getDeployTransaction(${firstMeta.ctorArgsExpr || ""});
|
|
1290
|
+
const est = await provider.estimateGas({ from: wallet.address, data: sampleReq.data });
|
|
1291
|
+
deployGasLimit = Number(est + 200_000n);
|
|
1292
|
+
} catch {
|
|
1293
|
+
deployGasLimit = 6_000_000;
|
|
1294
|
+
}
|
|
1295
|
+
${deployAndInvokeBlocks}
|
|
1296
|
+
}, { timeout: ${Math.max(900_000, 300_000 * artifacts.length)}});
|
|
1297
|
+
});
|
|
1298
|
+
`;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1098
1301
|
/**
|
|
1099
1302
|
* @typedef {Object} GenerateOptions
|
|
1100
1303
|
* @property {string} abiPath
|
|
@@ -1197,5 +1400,5 @@ function generate(opts) {
|
|
|
1197
1400
|
return { contractFile, factoryFile, typesFile: res.typesFile, indexFile: res.indexFile };
|
|
1198
1401
|
}
|
|
1199
1402
|
|
|
1200
|
-
module.exports = { generate, generateFromArtifacts, generateTransactionalTestJs };
|
|
1403
|
+
module.exports = { generate, generateFromArtifacts, generateTransactionalTestJs, generateAllContractsTransactionalTestJs };
|
|
1201
1404
|
|
|
@@ -71,6 +71,13 @@ export class AbstractProvider extends Provider {
|
|
|
71
71
|
* @returns {Promise<TransactionResponse>}
|
|
72
72
|
*/
|
|
73
73
|
sendTransaction(tx: TransactionRequest | string): Promise<TransactionResponse>;
|
|
74
|
+
/**
|
|
75
|
+
* Broadcast a signed raw transaction.
|
|
76
|
+
* Alias of sendTransaction(rawTx) for clarity in offline signing flows.
|
|
77
|
+
* @param {string} rawTx
|
|
78
|
+
* @returns {Promise<TransactionResponse>}
|
|
79
|
+
*/
|
|
80
|
+
sendRawTransaction(rawTx: string): Promise<TransactionResponse>;
|
|
74
81
|
/**
|
|
75
82
|
* Perform a call (read-only) and return hex data.
|
|
76
83
|
* @param {TransactionRequest} tx
|
|
@@ -296,6 +296,18 @@ class AbstractProvider extends Provider {
|
|
|
296
296
|
return result || new TransactionResponse({ hash }, this);
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Broadcast a signed raw transaction.
|
|
301
|
+
* Alias of sendTransaction(rawTx) for clarity when doing offline signing flows.
|
|
302
|
+
*
|
|
303
|
+
* @param {string} rawTx
|
|
304
|
+
* @returns {Promise<TransactionResponse>}
|
|
305
|
+
*/
|
|
306
|
+
async sendRawTransaction(rawTx) {
|
|
307
|
+
assertArgument(typeof rawTx === "string", "invalid rawTx", "rawTx", rawTx);
|
|
308
|
+
return this.sendTransaction(rawTx);
|
|
309
|
+
}
|
|
310
|
+
|
|
299
311
|
/**
|
|
300
312
|
* Perform a call (read-only) and return hex data.
|
|
301
313
|
* @param {TransactionRequest} tx
|
|
@@ -344,6 +344,13 @@ describe("AllSolidityTypes generated SDKs (extra tests)", () => {
|
|
|
344
344
|
const jsRun = runNpm(["test"], jsPkg, env);
|
|
345
345
|
assert.equal(jsRun.status, 0, `JS package tests failed:\n${jsRun.stdout}\n${jsRun.stderr}`);
|
|
346
346
|
|
|
347
|
+
// Run the generated offline signing example (offline deploy via sendRawTransaction).
|
|
348
|
+
const tsExample = run(process.execPath, [path.join(tsPkg, "examples", "offline-signing.js")], tsPkg, env);
|
|
349
|
+
assert.equal(tsExample.status, 0, `TS offline-signing example failed:\n${tsExample.stdout}\n${tsExample.stderr}`);
|
|
350
|
+
|
|
351
|
+
const jsExample = run(process.execPath, [path.join(jsPkg, "examples", "offline-signing.js")], jsPkg, env);
|
|
352
|
+
assert.equal(jsExample.status, 0, `JS offline-signing example failed:\n${jsExample.stdout}\n${jsExample.stderr}`);
|
|
353
|
+
|
|
347
354
|
succeeded = true;
|
|
348
355
|
} finally {
|
|
349
356
|
// We intentionally keep the generated SDK packages under test/e2e.
|
|
@@ -129,6 +129,13 @@ describe("SimpleERC20 generated SDKs", () => {
|
|
|
129
129
|
const jsRun = runNpm(["test"], jsPkg, env);
|
|
130
130
|
assert.equal(jsRun.status, 0, `JS package tests failed:\n${jsRun.stdout}\n${jsRun.stderr}`);
|
|
131
131
|
|
|
132
|
+
// Run the generated offline signing example (deploy + approve via sendRawTransaction).
|
|
133
|
+
const tsExample = run(process.execPath, [path.join(tsPkg, "examples", "offline-signing.js")], tsPkg, env);
|
|
134
|
+
assert.equal(tsExample.status, 0, `TS offline-signing example failed:\n${tsExample.stdout}\n${tsExample.stderr}`);
|
|
135
|
+
|
|
136
|
+
const jsExample = run(process.execPath, [path.join(jsPkg, "examples", "offline-signing.js")], jsPkg, env);
|
|
137
|
+
assert.equal(jsExample.status, 0, `JS offline-signing example failed:\n${jsExample.stdout}\n${jsExample.stderr}`);
|
|
138
|
+
|
|
132
139
|
succeeded = true;
|
|
133
140
|
} finally {
|
|
134
141
|
if (succeeded) {
|
|
@@ -113,9 +113,9 @@ describe("Transactional E2E (write)", () => {
|
|
|
113
113
|
// feePaid ~= gasLimit * fixedGasPrice
|
|
114
114
|
const expectedFee = BigInt(gasLimit) * FIXED_GAS_PRICE_WEI;
|
|
115
115
|
const feePaid = senderDelta - sendValue;
|
|
116
|
-
// Allow a wide tolerance (network/client may differ)
|
|
116
|
+
// Allow a wide tolerance (network/client gas price may differ significantly).
|
|
117
117
|
assert.ok(feePaid > 0n);
|
|
118
|
-
assert.ok(feePaid <= expectedFee *
|
|
118
|
+
assert.ok(feePaid <= expectedFee * 20n);
|
|
119
119
|
|
|
120
120
|
// Nonce should increment by at least 1.
|
|
121
121
|
assert.ok(nonceAfter >= nonceBefore + 1);
|
|
@@ -161,7 +161,7 @@ describe("Transactional E2E (write)", () => {
|
|
|
161
161
|
value: 0n,
|
|
162
162
|
});
|
|
163
163
|
|
|
164
|
-
const receipt = await deployTx.wait(1,
|
|
164
|
+
const receipt = await deployTx.wait(1, 600_000);
|
|
165
165
|
assert.ok(receipt);
|
|
166
166
|
if (receipt.contractAddress) {
|
|
167
167
|
assert.equal(receipt.contractAddress.toLowerCase(), expectedAddress.toLowerCase());
|
|
@@ -186,6 +186,6 @@ describe("Transactional E2E (write)", () => {
|
|
|
186
186
|
// qcsdk decode may return string/number; normalize to BigInt for comparison
|
|
187
187
|
const afterBI = typeof afterValue === "bigint" ? afterValue : BigInt(afterValue);
|
|
188
188
|
assert.equal(afterBI, 42n);
|
|
189
|
-
}, { timeout:
|
|
189
|
+
}, { timeout: 900_000 });
|
|
190
190
|
});
|
|
191
191
|
|
|
@@ -264,6 +264,7 @@ describe("typed contract generator package e2e", () => {
|
|
|
264
264
|
// Verify tests exist for each contract
|
|
265
265
|
assert.ok(fs.existsSync(path.join(pkgRoot, "test", "e2e", "Alpha.e2e.test.js")));
|
|
266
266
|
assert.ok(fs.existsSync(path.join(pkgRoot, "test", "e2e", "ConstructorParam.e2e.test.js")));
|
|
267
|
+
assert.ok(fs.existsSync(path.join(pkgRoot, "test", "e2e", "all-contracts.e2e.test.js")));
|
|
267
268
|
|
|
268
269
|
// Multi-contract packages use per-contract example filenames.
|
|
269
270
|
assert.ok(fs.existsSync(path.join(pkgRoot, "examples", "deploy-Alpha.js")));
|
|
@@ -368,6 +369,7 @@ describe("typed contract generator package e2e", () => {
|
|
|
368
369
|
// Verify tests exist for each contract
|
|
369
370
|
assert.ok(fs.existsSync(path.join(pkgRoot, "test", "e2e", "Alpha.e2e.test.js")));
|
|
370
371
|
assert.ok(fs.existsSync(path.join(pkgRoot, "test", "e2e", "Beta.e2e.test.js")));
|
|
372
|
+
assert.ok(fs.existsSync(path.join(pkgRoot, "test", "e2e", "all-contracts.e2e.test.js")));
|
|
371
373
|
|
|
372
374
|
// Verify Solidity comments were propagated into generated TS
|
|
373
375
|
const alphaTs = fs.readFileSync(path.join(pkgRoot, "src", "Alpha.ts"), "utf8");
|
|
@@ -10,7 +10,7 @@ const assert = require("node:assert/strict");
|
|
|
10
10
|
|
|
11
11
|
const qc = require("../../index");
|
|
12
12
|
|
|
13
|
-
const WS = "ws://127.0.0.1:8546";
|
|
13
|
+
const WS = process.env.QC_WS_URL || "ws://127.0.0.1:8546";
|
|
14
14
|
|
|
15
15
|
describe("WebSocketProvider (readonly)", () => {
|
|
16
16
|
it("getBlockNumber and getBlock('latest') work over WebSocket", async (t) => {
|
|
@@ -25,7 +25,7 @@ describe("generate-sdk.js CLI", () => {
|
|
|
25
25
|
|
|
26
26
|
assert.equal(res.status, 0);
|
|
27
27
|
const out = `${res.stdout || ""}${res.stderr || ""}`;
|
|
28
|
-
assert.ok(out.includes("
|
|
28
|
+
assert.ok(out.includes("sdkgen"));
|
|
29
29
|
assert.ok(out.includes("INPUT MODES"));
|
|
30
30
|
assert.ok(out.includes("--abi"));
|
|
31
31
|
assert.ok(out.includes("--bin"));
|
|
@@ -53,6 +53,7 @@ describe("typed contract generator", () => {
|
|
|
53
53
|
assert.ok(contractSrc.includes("export class TestToken"));
|
|
54
54
|
assert.ok(contractSrc.includes("async balanceOf"));
|
|
55
55
|
assert.ok(contractSrc.includes("async transfer"));
|
|
56
|
+
assert.ok(contractSrc.includes("populateTransaction"));
|
|
56
57
|
|
|
57
58
|
const factorySrc = fs.readFileSync(res.factoryFile, "utf8");
|
|
58
59
|
assert.ok(factorySrc.includes("export class TestToken__factory"));
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @testCategory unit
|
|
3
|
+
* @blockchainRequired false
|
|
4
|
+
* @transactional false
|
|
5
|
+
* @description populateTransaction namespace and sendRawTransaction API
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { describe, it } = require("node:test");
|
|
9
|
+
const assert = require("node:assert/strict");
|
|
10
|
+
|
|
11
|
+
const { Initialize } = require("../../config");
|
|
12
|
+
const qc = require("../../index");
|
|
13
|
+
|
|
14
|
+
describe("populateTransaction + sendRawTransaction", () => {
|
|
15
|
+
it("contract.populateTransaction.<fn> builds tx request without provider/signer", async () => {
|
|
16
|
+
await Initialize(null);
|
|
17
|
+
|
|
18
|
+
const abi = [
|
|
19
|
+
{
|
|
20
|
+
type: "function",
|
|
21
|
+
name: "setValue",
|
|
22
|
+
stateMutability: "nonpayable",
|
|
23
|
+
inputs: [{ name: "value", type: "uint256" }],
|
|
24
|
+
outputs: [],
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const addr = "0x" + "11".repeat(32);
|
|
29
|
+
const contract = new qc.Contract(addr, abi);
|
|
30
|
+
|
|
31
|
+
const tx = await contract.populateTransaction.setValue(123, { gasLimit: 50000 });
|
|
32
|
+
assert.equal(tx.to, addr);
|
|
33
|
+
assert.equal(typeof tx.data, "string");
|
|
34
|
+
assert.ok(tx.data.startsWith("0x") && tx.data.length > 10);
|
|
35
|
+
assert.equal(tx.gasLimit, 50000);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("provider.sendRawTransaction exists and forwards to eth_sendRawTransaction", async () => {
|
|
39
|
+
await Initialize(null);
|
|
40
|
+
|
|
41
|
+
class TestProvider extends qc.AbstractProvider {
|
|
42
|
+
constructor() {
|
|
43
|
+
super();
|
|
44
|
+
this.calls = [];
|
|
45
|
+
}
|
|
46
|
+
async _perform(method, params) {
|
|
47
|
+
this.calls.push({ method, params });
|
|
48
|
+
if (method === "eth_sendRawTransaction") return "0x" + "aa".repeat(32);
|
|
49
|
+
if (method === "eth_getTransactionByHash") return null;
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const p = new TestProvider();
|
|
55
|
+
const raw = "0xdeadbeef";
|
|
56
|
+
const resp = await p.sendRawTransaction(raw);
|
|
57
|
+
assert.ok(resp && typeof resp.hash === "string");
|
|
58
|
+
assert.equal(p.calls[0].method, "eth_sendRawTransaction");
|
|
59
|
+
assert.deepEqual(p.calls[0].params, [raw]);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|