quantum-coin-js-sdk 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/index.d.ts +9 -0
- package/index.js +37 -4
- package/package.json +12 -4
- package/wasmBase64.js +3 -2
- package/.idea/.name +0 -1
- package/.idea/modules.xml +0 -8
- package/.idea/quantum-coin-js-sdk.iml +0 -12
- package/.idea/vcs.xml +0 -6
- package/.vs/ProjectSettings.json +0 -3
- package/.vs/VSWorkspaceState.json +0 -8
- package/.vs/quantum-coin-js-sdk/FileContentIndex/a9d6de82-cfc3-4c22-ac38-06d706ff3673.vsidx +0 -0
- package/.vs/quantum-coin-js-sdk/FileContentIndex/fdc816df-1898-4153-99f2-c69ef4c2283b.vsidx +0 -0
- package/.vs/quantum-coin-js-sdk/config/applicationhost.config +0 -1011
- package/.vs/quantum-coin-js-sdk/v17/.wsuo +0 -0
- package/.vs/quantum-coin-js-sdk/v17/DocumentLayout.backup.json +0 -39
- package/.vs/quantum-coin-js-sdk/v17/DocumentLayout.json +0 -57
- package/.vs/slnx.sqlite +0 -0
- package/example/README.md +0 -14
- package/example/conversion-example.js +0 -19
- package/example/example-create-contract.js +0 -396
- package/example/example-encode-decode-rlp.js +0 -225
- package/example/example-event-pack-unpack.js +0 -391
- package/example/example-misc.js +0 -96
- package/example/example-token-pack-unpack.js +0 -960
- package/example/example-wallet-version4.js +0 -34
- package/example/example-wallet.js +0 -39
- package/example/example.js +0 -115
- package/example/node_modules/.bin/sdkgen +0 -16
- package/example/node_modules/.bin/sdkgen.cmd +0 -17
- package/example/node_modules/.bin/sdkgen.ps1 +0 -28
- package/example/node_modules/.package-lock.json +0 -51
- package/example/node_modules/quantumcoin/.github/workflows/publish-npmjs.yaml +0 -22
- package/example/node_modules/quantumcoin/LICENSE +0 -21
- package/example/node_modules/quantumcoin/README-SDK.md +0 -828
- package/example/node_modules/quantumcoin/README.md +0 -197
- package/example/node_modules/quantumcoin/config.d.ts +0 -50
- package/example/node_modules/quantumcoin/config.js +0 -123
- package/example/node_modules/quantumcoin/examples/AllSolidityTypes.sol +0 -184
- package/example/node_modules/quantumcoin/examples/SimpleIERC20.sol +0 -74
- package/example/node_modules/quantumcoin/examples/events.js +0 -41
- package/example/node_modules/quantumcoin/examples/events.ts +0 -35
- package/example/node_modules/quantumcoin/examples/example-generator-sdk-js.js +0 -100
- package/example/node_modules/quantumcoin/examples/example-generator-sdk-js.ts +0 -77
- package/example/node_modules/quantumcoin/examples/example-generator-sdk-ts.js +0 -100
- package/example/node_modules/quantumcoin/examples/example-generator-sdk-ts.ts +0 -77
- package/example/node_modules/quantumcoin/examples/example.js +0 -67
- package/example/node_modules/quantumcoin/examples/example.ts +0 -56
- package/example/node_modules/quantumcoin/examples/offline-signing.js +0 -77
- package/example/node_modules/quantumcoin/examples/offline-signing.ts +0 -65
- package/example/node_modules/quantumcoin/examples/package-lock.json +0 -596
- package/example/node_modules/quantumcoin/examples/package.json +0 -32
- package/example/node_modules/quantumcoin/examples/read-operations.js +0 -32
- package/example/node_modules/quantumcoin/examples/read-operations.ts +0 -31
- package/example/node_modules/quantumcoin/examples/sdk-generator-erc20.inline.json +0 -251
- package/example/node_modules/quantumcoin/examples/solidity-types.ts +0 -43
- package/example/node_modules/quantumcoin/examples/wallet-offline.js +0 -36
- package/example/node_modules/quantumcoin/examples/wallet-offline.ts +0 -26
- package/example/node_modules/quantumcoin/generate-sdk.js +0 -1846
- package/example/node_modules/quantumcoin/index.js +0 -12
- package/example/node_modules/quantumcoin/package.json +0 -95
- package/example/node_modules/quantumcoin/scripts/copy-declarations.js +0 -31
- package/example/node_modules/quantumcoin/scripts/run-all-one-by-one.js +0 -151
- package/example/node_modules/quantumcoin/src/abi/fragments.d.ts +0 -42
- package/example/node_modules/quantumcoin/src/abi/fragments.js +0 -63
- package/example/node_modules/quantumcoin/src/abi/index.d.ts +0 -13
- package/example/node_modules/quantumcoin/src/abi/index.js +0 -9
- package/example/node_modules/quantumcoin/src/abi/interface.d.ts +0 -128
- package/example/node_modules/quantumcoin/src/abi/interface.js +0 -605
- package/example/node_modules/quantumcoin/src/abi/js-abi-coder.d.ts +0 -8
- package/example/node_modules/quantumcoin/src/abi/js-abi-coder.js +0 -538
- package/example/node_modules/quantumcoin/src/constants.d.ts +0 -61
- package/example/node_modules/quantumcoin/src/constants.js +0 -94
- package/example/node_modules/quantumcoin/src/contract/contract-factory.d.ts +0 -28
- package/example/node_modules/quantumcoin/src/contract/contract-factory.js +0 -111
- package/example/node_modules/quantumcoin/src/contract/contract.d.ts +0 -113
- package/example/node_modules/quantumcoin/src/contract/contract.js +0 -408
- package/example/node_modules/quantumcoin/src/contract/index.d.ts +0 -9
- package/example/node_modules/quantumcoin/src/contract/index.js +0 -9
- package/example/node_modules/quantumcoin/src/errors/index.d.ts +0 -92
- package/example/node_modules/quantumcoin/src/errors/index.js +0 -200
- package/example/node_modules/quantumcoin/src/generator/index.d.ts +0 -81
- package/example/node_modules/quantumcoin/src/generator/index.js +0 -1571
- package/example/node_modules/quantumcoin/src/index.d.ts +0 -122
- package/example/node_modules/quantumcoin/src/index.js +0 -41
- package/example/node_modules/quantumcoin/src/internal/hex.d.ts +0 -68
- package/example/node_modules/quantumcoin/src/internal/hex.js +0 -180
- package/example/node_modules/quantumcoin/src/providers/extra-providers.d.ts +0 -139
- package/example/node_modules/quantumcoin/src/providers/extra-providers.js +0 -614
- package/example/node_modules/quantumcoin/src/providers/index.d.ts +0 -17
- package/example/node_modules/quantumcoin/src/providers/index.js +0 -10
- package/example/node_modules/quantumcoin/src/providers/json-rpc-provider.d.ts +0 -12
- package/example/node_modules/quantumcoin/src/providers/json-rpc-provider.js +0 -89
- package/example/node_modules/quantumcoin/src/providers/provider.d.ts +0 -206
- package/example/node_modules/quantumcoin/src/providers/provider.js +0 -524
- package/example/node_modules/quantumcoin/src/types/index.d.ts +0 -214
- package/example/node_modules/quantumcoin/src/types/index.js +0 -9
- package/example/node_modules/quantumcoin/src/utils/address.d.ts +0 -58
- package/example/node_modules/quantumcoin/src/utils/address.js +0 -144
- package/example/node_modules/quantumcoin/src/utils/encoding.d.ts +0 -120
- package/example/node_modules/quantumcoin/src/utils/encoding.js +0 -306
- package/example/node_modules/quantumcoin/src/utils/fixednumber.d.ts +0 -57
- package/example/node_modules/quantumcoin/src/utils/fixednumber.js +0 -366
- package/example/node_modules/quantumcoin/src/utils/hashing.d.ts +0 -76
- package/example/node_modules/quantumcoin/src/utils/hashing.js +0 -298
- package/example/node_modules/quantumcoin/src/utils/index.d.ts +0 -63
- package/example/node_modules/quantumcoin/src/utils/index.js +0 -14
- package/example/node_modules/quantumcoin/src/utils/result.d.ts +0 -57
- package/example/node_modules/quantumcoin/src/utils/result.js +0 -128
- package/example/node_modules/quantumcoin/src/utils/rlp.d.ts +0 -12
- package/example/node_modules/quantumcoin/src/utils/rlp.js +0 -215
- package/example/node_modules/quantumcoin/src/utils/units.d.ts +0 -29
- package/example/node_modules/quantumcoin/src/utils/units.js +0 -107
- package/example/node_modules/quantumcoin/src/wallet/index.d.ts +0 -10
- package/example/node_modules/quantumcoin/src/wallet/index.js +0 -8
- package/example/node_modules/quantumcoin/src/wallet/wallet.d.ts +0 -192
- package/example/node_modules/quantumcoin/src/wallet/wallet.js +0 -713
- package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.dynamic.test.js +0 -207
- package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.dynamic.test.ts +0 -191
- package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.fixtures.js +0 -231
- package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.generated-sdks.e2e.test.js +0 -387
- package/example/node_modules/quantumcoin/test/e2e/all-solidity-types.generated-sdks.e2e.test.ts +0 -350
- package/example/node_modules/quantumcoin/test/e2e/generator-interface.e2e.test.js +0 -165
- package/example/node_modules/quantumcoin/test/e2e/generator-interface.e2e.test.ts +0 -160
- package/example/node_modules/quantumcoin/test/e2e/helpers.js +0 -59
- package/example/node_modules/quantumcoin/test/e2e/signing-context-and-fee.e2e.test.js +0 -141
- package/example/node_modules/quantumcoin/test/e2e/signing-context-and-fee.e2e.test.ts +0 -128
- package/example/node_modules/quantumcoin/test/e2e/simple-erc20.generated-sdks.e2e.test.js +0 -168
- package/example/node_modules/quantumcoin/test/e2e/simple-erc20.generated-sdks.e2e.test.ts +0 -141
- package/example/node_modules/quantumcoin/test/e2e/transactional.test.js +0 -245
- package/example/node_modules/quantumcoin/test/e2e/transactional.test.ts +0 -208
- package/example/node_modules/quantumcoin/test/e2e/typed-generator.e2e.test.js +0 -407
- package/example/node_modules/quantumcoin/test/e2e/typed-generator.e2e.test.ts +0 -337
- package/example/node_modules/quantumcoin/test/fixtures/ConstructorParam.sol +0 -23
- package/example/node_modules/quantumcoin/test/fixtures/MultiContracts.sol +0 -37
- package/example/node_modules/quantumcoin/test/fixtures/SimpleStorage.sol +0 -18
- package/example/node_modules/quantumcoin/test/fixtures/StakingContract.abi.json +0 -1
- package/example/node_modules/quantumcoin/test/integration/ipc-provider.test.js +0 -49
- package/example/node_modules/quantumcoin/test/integration/ipc-provider.test.ts +0 -44
- package/example/node_modules/quantumcoin/test/integration/provider.test.js +0 -88
- package/example/node_modules/quantumcoin/test/integration/provider.test.ts +0 -85
- package/example/node_modules/quantumcoin/test/integration/ws-provider.test.js +0 -41
- package/example/node_modules/quantumcoin/test/integration/ws-provider.test.ts +0 -38
- package/example/node_modules/quantumcoin/test/security/abi-decoder-bounds.test.js +0 -122
- package/example/node_modules/quantumcoin/test/security/contract-overrides.test.js +0 -112
- package/example/node_modules/quantumcoin/test/security/generator-injection.test.js +0 -195
- package/example/node_modules/quantumcoin/test/security/malformed-input.test.js +0 -330
- package/example/node_modules/quantumcoin/test/security/malformed-input.test.ts +0 -35
- package/example/node_modules/quantumcoin/test/security/rpc-numeric-bounds.test.js +0 -81
- package/example/node_modules/quantumcoin/test/security/rpc-trust.test.js +0 -202
- package/example/node_modules/quantumcoin/test/unit/_encrypted-output.txt +0 -6
- package/example/node_modules/quantumcoin/test/unit/_log-encrypted-jsons.js +0 -45
- package/example/node_modules/quantumcoin/test/unit/_write-keystore-fixture.js +0 -16
- package/example/node_modules/quantumcoin/test/unit/abi-interface.test.js +0 -110
- package/example/node_modules/quantumcoin/test/unit/abi-interface.test.ts +0 -109
- package/example/node_modules/quantumcoin/test/unit/address-wallet.test.js +0 -923
- package/example/node_modules/quantumcoin/test/unit/address-wallet.test.ts +0 -877
- package/example/node_modules/quantumcoin/test/unit/browser-provider.test.js +0 -85
- package/example/node_modules/quantumcoin/test/unit/browser-provider.test.ts +0 -79
- package/example/node_modules/quantumcoin/test/unit/contract.test.js +0 -85
- package/example/node_modules/quantumcoin/test/unit/contract.test.ts +0 -83
- package/example/node_modules/quantumcoin/test/unit/encoding-units-rlp.test.js +0 -127
- package/example/node_modules/quantumcoin/test/unit/encoding-units-rlp.test.ts +0 -91
- package/example/node_modules/quantumcoin/test/unit/errors.test.js +0 -77
- package/example/node_modules/quantumcoin/test/unit/errors.test.ts +0 -76
- package/example/node_modules/quantumcoin/test/unit/filter-by-blockhash.test.js +0 -55
- package/example/node_modules/quantumcoin/test/unit/filter-by-blockhash.test.ts +0 -54
- package/example/node_modules/quantumcoin/test/unit/fixednumber.test.js +0 -656
- package/example/node_modules/quantumcoin/test/unit/fixednumber.test.ts +0 -660
- package/example/node_modules/quantumcoin/test/unit/fixtures/encrypted-keystores-48-32-36.js +0 -9
- package/example/node_modules/quantumcoin/test/unit/generate-contract-cli.test.js +0 -42
- package/example/node_modules/quantumcoin/test/unit/generate-contract-cli.test.ts +0 -41
- package/example/node_modules/quantumcoin/test/unit/generate-sdk-artifacts-json.test.js +0 -113
- package/example/node_modules/quantumcoin/test/unit/generate-sdk-artifacts-json.test.ts +0 -110
- package/example/node_modules/quantumcoin/test/unit/generator.test.js +0 -149
- package/example/node_modules/quantumcoin/test/unit/generator.test.ts +0 -148
- package/example/node_modules/quantumcoin/test/unit/hashing.test.js +0 -64
- package/example/node_modules/quantumcoin/test/unit/hashing.test.ts +0 -63
- package/example/node_modules/quantumcoin/test/unit/init.test.js +0 -39
- package/example/node_modules/quantumcoin/test/unit/init.test.ts +0 -38
- package/example/node_modules/quantumcoin/test/unit/interface.test.js +0 -56
- package/example/node_modules/quantumcoin/test/unit/interface.test.ts +0 -54
- package/example/node_modules/quantumcoin/test/unit/internal-hex.test.js +0 -81
- package/example/node_modules/quantumcoin/test/unit/internal-hex.test.ts +0 -80
- package/example/node_modules/quantumcoin/test/unit/populate-transaction.test.js +0 -98
- package/example/node_modules/quantumcoin/test/unit/populate-transaction.test.ts +0 -64
- package/example/node_modules/quantumcoin/test/unit/providers.test.js +0 -252
- package/example/node_modules/quantumcoin/test/unit/providers.test.ts +0 -251
- package/example/node_modules/quantumcoin/test/unit/result.test.js +0 -80
- package/example/node_modules/quantumcoin/test/unit/result.test.ts +0 -79
- package/example/node_modules/quantumcoin/test/unit/solidity-types.test.js +0 -49
- package/example/node_modules/quantumcoin/test/unit/solidity-types.test.ts +0 -39
- package/example/node_modules/quantumcoin/test/unit/utils.test.js +0 -57
- package/example/node_modules/quantumcoin/test/unit/utils.test.ts +0 -56
- package/example/node_modules/quantumcoin/test/verbose-logger.js +0 -74
- package/example/node_modules/quantumcoin/tsconfig.build.json +0 -14
- package/example/node_modules/seed-words/.github/workflows/publish-npmjs.yaml +0 -22
- package/example/node_modules/seed-words/BUILD.md +0 -7
- package/example/node_modules/seed-words/LICENSE +0 -121
- package/example/node_modules/seed-words/README.md +0 -67
- package/example/node_modules/seed-words/dist/seedwords.d.ts +0 -39
- package/example/node_modules/seed-words/package.json +0 -27
- package/example/node_modules/seed-words/seedwords.js +0 -315
- package/example/node_modules/seed-words/seedwords.txt +0 -65536
- package/example/node_modules/seed-words/tsconfig.json +0 -21
- package/example/package-lock.json +0 -60
- package/example/package.json +0 -15
- package/scripts/build-browser-tests.js +0 -29
- package/scripts/build-wasm.js +0 -85
- package/tests/browser/bundle.js +0 -97643
- package/tests/browser/entry.js +0 -57
- package/tests/browser/harness.html +0 -20
- package/tests/browser/run-browser.js +0 -50
- package/tests/encrypted-32.json +0 -1
- package/tests/encrypted-36.json +0 -1
- package/tests/encrypted-48.json +0 -1
- package/tests/generate-verify-vectors.js +0 -88
- package/tests/get-gas-price.test.js +0 -59
- package/tests/hashing.test.js +0 -34
- package/tests/non-transactional.preinit.test.js +0 -33
- package/tests/non-transactional.test.js +0 -1318
- package/tests/sign-raw-keytype5-context-null.test.js +0 -105
- package/tests/sign-raw-transaction.test.js +0 -194
- package/tests/sign-verify.test.js +0 -308
- package/tests/specs/assert.js +0 -74
- package/tests/specs/crypto.spec.js +0 -104
- package/tests/specs/hex.js +0 -31
- package/tests/specs/preinit.spec.js +0 -19
- package/tests/specs/sign.spec.js +0 -90
- package/tests/specs/wallet.spec.js +0 -98
- package/tests/transactional.rpc.test.js +0 -102
- package/tests/verify-vectors.json +0 -95035
|
@@ -1,1571 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Typed contract generator (SPEC.md section 15).
|
|
3
|
-
*
|
|
4
|
-
* Supports generating:
|
|
5
|
-
* - TypeScript source (`.ts`)
|
|
6
|
-
* - JavaScript source (`.js`) + TypeScript declaration files (`.d.ts`)
|
|
7
|
-
*
|
|
8
|
-
* It is designed to be invoked by `generate-sdk.js` (CLI) and
|
|
9
|
-
* can also be imported as a library.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require("node:fs");
|
|
13
|
-
const path = require("node:path");
|
|
14
|
-
|
|
15
|
-
function _ensureDir(p) {
|
|
16
|
-
fs.mkdirSync(p, { recursive: true });
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function _readJson(filePath) {
|
|
20
|
-
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function _stripArraySuffixes(s) {
|
|
24
|
-
let out = String(s || "");
|
|
25
|
-
while (out.endsWith("]")) {
|
|
26
|
-
out = out.slice(0, out.lastIndexOf("["));
|
|
27
|
-
}
|
|
28
|
-
return out;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function _parseArray(type) {
|
|
32
|
-
const t = String(type || "");
|
|
33
|
-
const idx = t.lastIndexOf("[");
|
|
34
|
-
if (idx < 0 || !t.endsWith("]")) return null;
|
|
35
|
-
const inner = t.slice(0, idx);
|
|
36
|
-
const bracket = t.slice(idx + 1, t.length - 1); // "" => dynamic
|
|
37
|
-
const fixedLen = bracket.length ? Number(bracket) : null;
|
|
38
|
-
return { inner, fixedLen: fixedLen != null && Number.isFinite(fixedLen) ? fixedLen : null };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function _tupleBaseNameFromInternalType(contractName, internalType) {
|
|
42
|
-
const raw = String(internalType || "");
|
|
43
|
-
if (!raw) return null;
|
|
44
|
-
const cleaned = _stripArraySuffixes(raw);
|
|
45
|
-
const m = cleaned.match(/struct\s+([A-Za-z0-9_]+)\.([A-Za-z0-9_]+)/);
|
|
46
|
-
if (m && m[2]) return m[2];
|
|
47
|
-
// Some compilers omit "struct" keyword.
|
|
48
|
-
const m2 = cleaned.match(/^([A-Za-z0-9_]+)\.([A-Za-z0-9_]+)$/);
|
|
49
|
-
if (m2 && m2[2]) return m2[2];
|
|
50
|
-
// Fallback: last segment
|
|
51
|
-
const parts = cleaned.split(".");
|
|
52
|
-
const last = parts[parts.length - 1];
|
|
53
|
-
if (last && last !== contractName) return last;
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function _tupleKey(param) {
|
|
58
|
-
/** @param {any} p */
|
|
59
|
-
function norm(p) {
|
|
60
|
-
const out = {
|
|
61
|
-
name: String(p && p.name ? p.name : ""),
|
|
62
|
-
type: String(p && p.type ? p.type : ""),
|
|
63
|
-
internalType: String(p && p.internalType ? p.internalType : ""),
|
|
64
|
-
components: [],
|
|
65
|
-
};
|
|
66
|
-
const comps = Array.isArray(p && p.components) ? p.components : [];
|
|
67
|
-
out.components = comps.map((c) => norm(c));
|
|
68
|
-
return out;
|
|
69
|
-
}
|
|
70
|
-
return JSON.stringify(norm(param || {}));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function _collectTupleRegistry(contractName, abi) {
|
|
74
|
-
const byKey = new Map(); // key -> baseName
|
|
75
|
-
const usedNames = new Map(); // baseName -> key
|
|
76
|
-
let counter = 0;
|
|
77
|
-
|
|
78
|
-
/** @param {any} param */
|
|
79
|
-
function ensureTuple(param) {
|
|
80
|
-
const key = _tupleKey(param);
|
|
81
|
-
if (byKey.has(key)) return byKey.get(key);
|
|
82
|
-
|
|
83
|
-
const suggested = _safeTypeIdent(
|
|
84
|
-
_tupleBaseNameFromInternalType(contractName, param && param.internalType) || `${contractName}Tuple${++counter}`,
|
|
85
|
-
);
|
|
86
|
-
let baseName = suggested;
|
|
87
|
-
if (usedNames.has(baseName) && usedNames.get(baseName) !== key) {
|
|
88
|
-
let n = 1;
|
|
89
|
-
while (usedNames.has(`${suggested}_${n}`) && usedNames.get(`${suggested}_${n}`) !== key) n++;
|
|
90
|
-
baseName = `${suggested}_${n}`;
|
|
91
|
-
}
|
|
92
|
-
byKey.set(key, baseName);
|
|
93
|
-
usedNames.set(baseName, key);
|
|
94
|
-
|
|
95
|
-
// Recurse to nested tuples.
|
|
96
|
-
const comps = Array.isArray(param && param.components) ? param.components : [];
|
|
97
|
-
for (const c of comps) visitParam(c);
|
|
98
|
-
return baseName;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** @param {any} param */
|
|
102
|
-
function visitParam(param) {
|
|
103
|
-
const type = String(param && param.type ? param.type : "");
|
|
104
|
-
if (!type) return;
|
|
105
|
-
const arr = _parseArray(type);
|
|
106
|
-
if (arr) {
|
|
107
|
-
visitParam({ ...(param || {}), type: arr.inner });
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (type === "tuple") {
|
|
111
|
-
ensureTuple(param);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
for (const f of abi || []) {
|
|
117
|
-
if (!f || typeof f !== "object") continue;
|
|
118
|
-
const inputs = Array.isArray(f.inputs) ? f.inputs : [];
|
|
119
|
-
const outputs = Array.isArray(f.outputs) ? f.outputs : [];
|
|
120
|
-
for (const p of inputs) visitParam(p);
|
|
121
|
-
for (const p of outputs) visitParam(p);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return { byKey };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function _solParamToTs(param, mode, tupleReg) {
|
|
128
|
-
const type = String(param && param.type ? param.type : "");
|
|
129
|
-
const m = mode === "output" ? "output" : "input";
|
|
130
|
-
|
|
131
|
-
const arr = _parseArray(type);
|
|
132
|
-
if (arr) {
|
|
133
|
-
const innerParam = { ...(param || {}), type: arr.inner };
|
|
134
|
-
const innerTs = _solParamToTs(innerParam, mode, tupleReg);
|
|
135
|
-
if (arr.fixedLen != null) return `Types.SolFixedArray<${innerTs}, ${arr.fixedLen}>`;
|
|
136
|
-
return `Types.SolArray<${innerTs}>`;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (type === "tuple") {
|
|
140
|
-
const key = _tupleKey(param);
|
|
141
|
-
const baseName = tupleReg && tupleReg.byKey ? tupleReg.byKey.get(key) : null;
|
|
142
|
-
const n = baseName || "Tuple";
|
|
143
|
-
return `${n}${m === "input" ? "Input" : "Output"}`;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Elementary types (hard typed)
|
|
147
|
-
if (type === "address") return m === "input" ? "Types.AddressLike" : "Types.SolAddress";
|
|
148
|
-
if (type === "bool") return "boolean";
|
|
149
|
-
if (type === "string") return "string";
|
|
150
|
-
if (type === "bytes") return m === "input" ? "Types.BytesLike" : "Types.HexString";
|
|
151
|
-
|
|
152
|
-
const mBytesN = type.match(/^bytes(\d+)$/);
|
|
153
|
-
if (mBytesN) {
|
|
154
|
-
const n = Number(mBytesN[1]);
|
|
155
|
-
if (n === 32) return m === "input" ? "Types.Bytes32Like" : "Types.Bytes32";
|
|
156
|
-
if (Number.isFinite(n) && n >= 1 && n <= 32) return m === "input" ? `Types.Bytes${n}Like` : `Types.Bytes${n}`;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const mUint = type === "uint" ? ["uint", "256"] : type.match(/^uint(\d+)$/);
|
|
160
|
-
if (mUint) {
|
|
161
|
-
const bits = type === "uint" ? 256 : Number(mUint[1]);
|
|
162
|
-
const b = Number.isFinite(bits) ? bits : 256;
|
|
163
|
-
return m === "input" ? `Types.Uint${b}Like` : `Types.Uint${b}`;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const mInt = type === "int" ? ["int", "256"] : type.match(/^int(\d+)$/);
|
|
167
|
-
if (mInt) {
|
|
168
|
-
const bits = type === "int" ? 256 : Number(mInt[1]);
|
|
169
|
-
const b = Number.isFinite(bits) ? bits : 256;
|
|
170
|
-
return m === "input" ? `Types.Int${b}Like` : `Types.Int${b}`;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Fallback (unknown type)
|
|
174
|
-
return "any";
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function _solParamToJsDoc(param, mode, tupleReg) {
|
|
178
|
-
const type = String(param && param.type ? param.type : "");
|
|
179
|
-
const m = mode === "output" ? "output" : "input";
|
|
180
|
-
|
|
181
|
-
const arr = _parseArray(type);
|
|
182
|
-
if (arr) {
|
|
183
|
-
const innerParam = { ...(param || {}), type: arr.inner };
|
|
184
|
-
const inner = _solParamToJsDoc(innerParam, mode, tupleReg);
|
|
185
|
-
return `Array<${inner}>`;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (type === "tuple") {
|
|
189
|
-
const key = _tupleKey(param);
|
|
190
|
-
const baseName = tupleReg && tupleReg.byKey ? tupleReg.byKey.get(key) : null;
|
|
191
|
-
const n = baseName || "Tuple";
|
|
192
|
-
return `${n}${m === "input" ? "Input" : "Output"}`;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (type === "address") {
|
|
196
|
-
return m === "input" ? `import("quantumcoin/types").AddressLike` : `import("quantumcoin/types").SolAddress`;
|
|
197
|
-
}
|
|
198
|
-
if (type === "bool") return "boolean";
|
|
199
|
-
if (type === "string") return "string";
|
|
200
|
-
if (type === "bytes") {
|
|
201
|
-
return m === "input" ? `import("quantumcoin/types").BytesLike` : `import("quantumcoin/types").HexString`;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const mBytesN = type.match(/^bytes(\d+)$/);
|
|
205
|
-
if (mBytesN) {
|
|
206
|
-
const n = Number(mBytesN[1]);
|
|
207
|
-
if (n === 32) {
|
|
208
|
-
return m === "input" ? `import("quantumcoin/types").Bytes32Like` : `import("quantumcoin/types").Bytes32`;
|
|
209
|
-
}
|
|
210
|
-
if (Number.isFinite(n) && n >= 1 && n <= 32) {
|
|
211
|
-
return m === "input"
|
|
212
|
-
? `import("quantumcoin/types").Bytes${n}Like`
|
|
213
|
-
: `import("quantumcoin/types").Bytes${n}`;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const mUint = type === "uint" ? ["uint", "256"] : type.match(/^uint(\d+)$/);
|
|
218
|
-
if (mUint) {
|
|
219
|
-
const bits = type === "uint" ? 256 : Number(mUint[1]);
|
|
220
|
-
const b = Number.isFinite(bits) ? bits : 256;
|
|
221
|
-
return m === "input"
|
|
222
|
-
? `import("quantumcoin/types").Uint${b}Like`
|
|
223
|
-
: `import("quantumcoin/types").Uint${b}`;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const mInt = type === "int" ? ["int", "256"] : type.match(/^int(\d+)$/);
|
|
227
|
-
if (mInt) {
|
|
228
|
-
const bits = type === "int" ? 256 : Number(mInt[1]);
|
|
229
|
-
const b = Number.isFinite(bits) ? bits : 256;
|
|
230
|
-
return m === "input"
|
|
231
|
-
? `import("quantumcoin/types").Int${b}Like`
|
|
232
|
-
: `import("quantumcoin/types").Int${b}`;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return "any";
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function _renderTupleTypeDefs(contractName, abi, tupleReg) {
|
|
239
|
-
const lines = [];
|
|
240
|
-
void contractName;
|
|
241
|
-
|
|
242
|
-
function _findTupleParamByKey(key) {
|
|
243
|
-
/** @param {any} p */
|
|
244
|
-
function walkParam(p) {
|
|
245
|
-
if (!p || typeof p !== "object") return null;
|
|
246
|
-
const t = String(p.type || "");
|
|
247
|
-
if (!t) return null;
|
|
248
|
-
const arr = _parseArray(t);
|
|
249
|
-
if (arr) return walkParam({ ...(p || {}), type: arr.inner });
|
|
250
|
-
if (t === "tuple") {
|
|
251
|
-
if (_tupleKey(p) === key) return p;
|
|
252
|
-
const comps = Array.isArray(p.components) ? p.components : [];
|
|
253
|
-
for (const c of comps) {
|
|
254
|
-
const found = walkParam(c);
|
|
255
|
-
if (found) return found;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
for (const f of abi || []) {
|
|
262
|
-
if (!f || typeof f !== "object") continue;
|
|
263
|
-
const inputs = Array.isArray(f.inputs) ? f.inputs : [];
|
|
264
|
-
const outputs = Array.isArray(f.outputs) ? f.outputs : [];
|
|
265
|
-
for (const p of inputs) {
|
|
266
|
-
const found = walkParam(p);
|
|
267
|
-
if (found) return found;
|
|
268
|
-
}
|
|
269
|
-
for (const p of outputs) {
|
|
270
|
-
const found = walkParam(p);
|
|
271
|
-
if (found) return found;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
return null;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/** @param {any} param */
|
|
278
|
-
function tupleFields(param, mode) {
|
|
279
|
-
const comps = Array.isArray(param && param.components) ? param.components : [];
|
|
280
|
-
const fields = [];
|
|
281
|
-
for (let i = 0; i < comps.length; i++) {
|
|
282
|
-
const c = comps[i];
|
|
283
|
-
const field = _safeIdent((c && c.name) || `field${i}`);
|
|
284
|
-
fields.push(` ${field}: ${_solParamToTs(c, mode, tupleReg)};`);
|
|
285
|
-
}
|
|
286
|
-
return fields;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Render each known tuple once (as Input + Output).
|
|
290
|
-
for (const [key, baseName] of tupleReg.byKey.entries()) {
|
|
291
|
-
const param = _findTupleParamByKey(key);
|
|
292
|
-
if (!param) continue;
|
|
293
|
-
|
|
294
|
-
lines.push(`export type ${baseName}Input = {`);
|
|
295
|
-
lines.push(...tupleFields(param, "input"));
|
|
296
|
-
lines.push(`};`);
|
|
297
|
-
lines.push(``);
|
|
298
|
-
lines.push(`export type ${baseName}Output = {`);
|
|
299
|
-
lines.push(...tupleFields(param, "output"));
|
|
300
|
-
lines.push(`};`);
|
|
301
|
-
lines.push(``);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return lines.length ? lines.join("\n") + "\n" : "";
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function _renderTupleJsDocTypeDefs(contractName, abi, tupleReg) {
|
|
308
|
-
void contractName;
|
|
309
|
-
if (!tupleReg || !tupleReg.byKey || tupleReg.byKey.size === 0) return "";
|
|
310
|
-
|
|
311
|
-
const lines = [];
|
|
312
|
-
|
|
313
|
-
/** @param {string} key */
|
|
314
|
-
function findTupleParamByKey(key) {
|
|
315
|
-
/** @param {any} p */
|
|
316
|
-
function walkParam(p) {
|
|
317
|
-
if (!p || typeof p !== "object") return null;
|
|
318
|
-
const t = String(p.type || "");
|
|
319
|
-
if (!t) return null;
|
|
320
|
-
const arr = _parseArray(t);
|
|
321
|
-
if (arr) return walkParam({ ...(p || {}), type: arr.inner });
|
|
322
|
-
if (t === "tuple") {
|
|
323
|
-
if (_tupleKey(p) === key) return p;
|
|
324
|
-
const comps = Array.isArray(p.components) ? p.components : [];
|
|
325
|
-
for (const c of comps) {
|
|
326
|
-
const found = walkParam(c);
|
|
327
|
-
if (found) return found;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
return null;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
for (const f of abi || []) {
|
|
334
|
-
if (!f || typeof f !== "object") continue;
|
|
335
|
-
const inputs = Array.isArray(f.inputs) ? f.inputs : [];
|
|
336
|
-
const outputs = Array.isArray(f.outputs) ? f.outputs : [];
|
|
337
|
-
for (const p of inputs) {
|
|
338
|
-
const found = walkParam(p);
|
|
339
|
-
if (found) return found;
|
|
340
|
-
}
|
|
341
|
-
for (const p of outputs) {
|
|
342
|
-
const found = walkParam(p);
|
|
343
|
-
if (found) return found;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return null;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function renderTypedef(typeName, tupleParam, mode) {
|
|
350
|
-
const comps = Array.isArray(tupleParam && tupleParam.components) ? tupleParam.components : [];
|
|
351
|
-
lines.push("/**");
|
|
352
|
-
lines.push(` * @typedef {Object} ${typeName}`);
|
|
353
|
-
for (let i = 0; i < comps.length; i++) {
|
|
354
|
-
const c = comps[i];
|
|
355
|
-
const field = _safeIdent((c && c.name) || `field${i}`);
|
|
356
|
-
lines.push(` * @property {${_solParamToJsDoc(c, mode, tupleReg)}} ${field}`);
|
|
357
|
-
}
|
|
358
|
-
lines.push(" */");
|
|
359
|
-
lines.push("");
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
for (const [key, baseName] of tupleReg.byKey.entries()) {
|
|
363
|
-
const param = findTupleParamByKey(key);
|
|
364
|
-
if (!param) continue;
|
|
365
|
-
renderTypedef(`${baseName}Input`, param, "input");
|
|
366
|
-
renderTypedef(`${baseName}Output`, param, "output");
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
return lines.join("\n");
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
function _cap(s) {
|
|
373
|
-
return s ? s[0].toUpperCase() + s.slice(1) : s;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function _safeIdent(name) {
|
|
377
|
-
return (name || "arg").replace(/[^a-zA-Z0-9_]/g, "_");
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// NatSpec/doc text is attacker-controllable (it comes from arbitrary Solidity
|
|
381
|
-
// source). It is emitted into generated `/** ... */` JSDoc blocks, so a `*/` in the
|
|
382
|
-
// text would close the comment early and turn whatever follows into executable code
|
|
383
|
-
// in the generated .js/.ts files. Neutralize the comment delimiters (and strip
|
|
384
|
-
// CR/LF so a line cannot be split) before interpolation.
|
|
385
|
-
function _escapeComment(text) {
|
|
386
|
-
return String(text == null ? "" : text)
|
|
387
|
-
.replace(/\*\//g, "* /")
|
|
388
|
-
.replace(/\/\*/g, "/ *")
|
|
389
|
-
.replace(/[\r\n]+/g, " ");
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Reserved words / dangerous property names that must never be emitted as a raw
|
|
393
|
-
// identifier (class name, function name, import/require specifier, etc.).
|
|
394
|
-
const _RESERVED_IDENTIFIERS = new Set([
|
|
395
|
-
"break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do",
|
|
396
|
-
"else", "export", "extends", "finally", "for", "function", "if", "import", "in", "instanceof",
|
|
397
|
-
"new", "return", "super", "switch", "this", "throw", "try", "typeof", "var", "void", "while",
|
|
398
|
-
"with", "yield", "enum", "await", "implements", "interface", "let", "package", "private",
|
|
399
|
-
"protected", "public", "static", "null", "true", "false",
|
|
400
|
-
"__proto__", "constructor", "prototype",
|
|
401
|
-
]);
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
* Assert that `name` is safe to interpolate verbatim into generated source code
|
|
405
|
-
* (class names, function names, type names, require/import specifiers, file names).
|
|
406
|
-
*
|
|
407
|
-
* This is the primary defense against code-injection and path-traversal
|
|
408
|
-
* via attacker-controlled ABI / artifact `name` fields: only strict JS/TS
|
|
409
|
-
* identifiers are allowed, which by construction cannot contain quotes, newlines,
|
|
410
|
-
* path separators, `..`, or other breakout characters.
|
|
411
|
-
*
|
|
412
|
-
* @param {any} name
|
|
413
|
-
* @param {string=} kind Human-readable description for error messages.
|
|
414
|
-
* @returns {string} the validated name
|
|
415
|
-
*/
|
|
416
|
-
function _assertSafeIdentifier(name, kind) {
|
|
417
|
-
const label = kind || "identifier";
|
|
418
|
-
if (typeof name !== "string" || !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) {
|
|
419
|
-
throw new Error(`Unsafe ${label} for code generation: ${JSON.stringify(name)}`);
|
|
420
|
-
}
|
|
421
|
-
if (_RESERVED_IDENTIFIERS.has(name)) {
|
|
422
|
-
throw new Error(`${label} must not be a reserved word: ${JSON.stringify(name)}`);
|
|
423
|
-
}
|
|
424
|
-
return name;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* Coerce an arbitrary string (e.g. a struct name derived from an attacker-controlled
|
|
429
|
-
* `internalType`) into a safe type identifier. Unlike `_assertSafeIdentifier` this
|
|
430
|
-
* never throws; it sanitizes so auto-generated type names always remain valid.
|
|
431
|
-
* @param {any} s
|
|
432
|
-
* @returns {string}
|
|
433
|
-
*/
|
|
434
|
-
function _safeTypeIdent(s) {
|
|
435
|
-
let v = String(s == null ? "" : s).replace(/[^A-Za-z0-9_$]/g, "_");
|
|
436
|
-
if (!/^[A-Za-z_$]/.test(v)) v = "_" + v;
|
|
437
|
-
return v || "_Tuple";
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Validate every rendered function name in an ABI. Only `function` entries are
|
|
442
|
-
* emitted as method identifiers, so those are the names that must be safe.
|
|
443
|
-
* @param {any[]} abi
|
|
444
|
-
*/
|
|
445
|
-
function _assertSafeAbiNames(abi) {
|
|
446
|
-
for (const f of abi || []) {
|
|
447
|
-
if (f && f.type === "function" && f.name != null) {
|
|
448
|
-
_assertSafeIdentifier(f.name, "ABI function name");
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* Assert that `filePath` resolves to a location inside `outDir` (defense-in-depth).
|
|
455
|
-
* @param {string} outDir
|
|
456
|
-
* @param {string} filePath
|
|
457
|
-
*/
|
|
458
|
-
function _assertWithinDir(outDir, filePath) {
|
|
459
|
-
const root = path.resolve(outDir);
|
|
460
|
-
const resolved = path.resolve(filePath);
|
|
461
|
-
const rel = path.relative(root, resolved);
|
|
462
|
-
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
463
|
-
throw new Error(`Refusing to write outside output directory: ${resolved}`);
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
function _findConstructor(abi) {
|
|
468
|
-
const ctor = abi.find((f) => f && f.type === "constructor");
|
|
469
|
-
return ctor || { type: "constructor", inputs: [] };
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* Return true if the given bytecode artifact represents an interface
|
|
474
|
-
* (or otherwise non-deployable contract with no runtime code).
|
|
475
|
-
*
|
|
476
|
-
* `solc` writes an empty `.bin` for `interface` declarations and fully
|
|
477
|
-
* abstract `contract` declarations. We treat `null`, `undefined`, empty
|
|
478
|
-
* string, and the bare `"0x"` / `"0X"` prefix as interface bytecode.
|
|
479
|
-
*
|
|
480
|
-
* @param {string|null|undefined} bytecode
|
|
481
|
-
* @returns {boolean}
|
|
482
|
-
*/
|
|
483
|
-
function _isInterfaceBytecode(bytecode) {
|
|
484
|
-
if (bytecode == null) return true;
|
|
485
|
-
const s = String(bytecode).trim().toLowerCase();
|
|
486
|
-
return s === "" || s === "0x";
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function _solTypeToTestValueExpr(param) {
|
|
490
|
-
const type = typeof param === "string" ? param : String(param && param.type ? param.type : "");
|
|
491
|
-
const internalType = typeof param === "object" && param ? String(param.internalType || "") : "";
|
|
492
|
-
|
|
493
|
-
// Arrays (dynamic or fixed)
|
|
494
|
-
if (type.endsWith("]")) {
|
|
495
|
-
const inner = type.slice(0, type.lastIndexOf("["));
|
|
496
|
-
const bracket = type.slice(type.lastIndexOf("[") + 1, type.length - 1);
|
|
497
|
-
const isFixed = bracket.length > 0;
|
|
498
|
-
const fixedLen = isFixed ? Number(bracket) : 0;
|
|
499
|
-
const elemParam = { ...(param || {}), type: inner };
|
|
500
|
-
|
|
501
|
-
const elemExpr = _solTypeToTestValueExpr(elemParam);
|
|
502
|
-
if (isFixed && Number.isFinite(fixedLen) && fixedLen > 0) {
|
|
503
|
-
// Fixed arrays MUST match the exact declared length.
|
|
504
|
-
// Use Array(len).fill(expr) to keep source size reasonable.
|
|
505
|
-
return `Array(${fixedLen}).fill(${elemExpr})`;
|
|
506
|
-
}
|
|
507
|
-
return `[${elemExpr}]`;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
if (type === "address") return "wallet.address";
|
|
511
|
-
if (type === "bool") return "true";
|
|
512
|
-
if (type === "string") return JSON.stringify("hello");
|
|
513
|
-
if (type === "bytes") return JSON.stringify("0x1234");
|
|
514
|
-
|
|
515
|
-
// Fixed-size bytes
|
|
516
|
-
const mBytesN = type.match(/^bytes(\d+)$/);
|
|
517
|
-
if (mBytesN) {
|
|
518
|
-
const n = Number(mBytesN[1]);
|
|
519
|
-
if (Number.isFinite(n) && n >= 1 && n <= 32) return JSON.stringify(`0x${"11".repeat(n)}`);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// NOTE: quantum-coin-js-sdk WASM interop does not accept BigInt values directly.
|
|
523
|
-
// Use plain numbers/strings for ints/uints.
|
|
524
|
-
// Enums are ABI-encoded as uints but Solidity will revert if the value is out of range.
|
|
525
|
-
if (type.startsWith("uint") && /\benum\b/.test(internalType)) return "1";
|
|
526
|
-
if (type.startsWith("uint")) return "123";
|
|
527
|
-
if (type.startsWith("int")) return "-123";
|
|
528
|
-
|
|
529
|
-
// Tuple / struct
|
|
530
|
-
if (type === "tuple") {
|
|
531
|
-
const comps = Array.isArray(param && param.components) ? param.components : [];
|
|
532
|
-
if (comps.length === 0) return "{}";
|
|
533
|
-
const fields = comps.map((c, idx) => {
|
|
534
|
-
const name = c && typeof c.name === "string" && c.name ? c.name : `field${idx}`;
|
|
535
|
-
return `${JSON.stringify(name)}: ${_solTypeToTestValueExpr(c)}`;
|
|
536
|
-
});
|
|
537
|
-
return `{ ${fields.join(", ")} }`;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// Fallback
|
|
541
|
-
return "undefined";
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
function _isSupportedType(type) {
|
|
545
|
-
if (type.endsWith("]")) return _isSupportedType(type.slice(0, type.lastIndexOf("[")));
|
|
546
|
-
return (
|
|
547
|
-
type === "address" ||
|
|
548
|
-
type === "bool" ||
|
|
549
|
-
type === "string" ||
|
|
550
|
-
type === "bytes" ||
|
|
551
|
-
type === "bytes32" ||
|
|
552
|
-
type.startsWith("uint") ||
|
|
553
|
-
type.startsWith("int")
|
|
554
|
-
);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
function _allSupportedParams(inputs) {
|
|
558
|
-
return (inputs || []).every((i) => i && typeof i.type === "string" && _isSupportedType(i.type));
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
function _typesTs() {
|
|
562
|
-
return (
|
|
563
|
-
`// Auto-generated by sdkgen\n\n` +
|
|
564
|
-
`// Re-export ALL Solidity-related types from quantumcoin.\n` +
|
|
565
|
-
`export type * from "quantumcoin/types";\n`
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
function _typesJs() {
|
|
570
|
-
return `// Auto-generated by sdkgen\n\n` + `module.exports = {};\n`;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
function _typesDts() {
|
|
574
|
-
return (
|
|
575
|
-
`// Auto-generated by sdkgen\n\n` +
|
|
576
|
-
`export type * from "quantumcoin/types";\n`
|
|
577
|
-
);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
function _renderContractTs({ contractName, abi, bytecode, docs }) {
|
|
581
|
-
const functions = (abi || []).filter((f) => f && f.type === "function");
|
|
582
|
-
const tupleReg = _collectTupleRegistry(contractName, abi);
|
|
583
|
-
const txFns = functions.filter((f) => (f.stateMutability || "") !== "view" && (f.stateMutability || "") !== "pure");
|
|
584
|
-
|
|
585
|
-
const contractTsLines = [];
|
|
586
|
-
contractTsLines.push(`// Auto-generated by sdkgen`);
|
|
587
|
-
contractTsLines.push(`import { Contract, ContractTransactionResponse, ContractRunner, TransactionResponse } from "quantumcoin";`);
|
|
588
|
-
contractTsLines.push(`import type * as Types from "./types";`);
|
|
589
|
-
contractTsLines.push(``);
|
|
590
|
-
contractTsLines.push(_renderTupleTypeDefs(contractName, abi, tupleReg).trimEnd());
|
|
591
|
-
contractTsLines.push(``);
|
|
592
|
-
contractTsLines.push(`/**`);
|
|
593
|
-
contractTsLines.push(` * ${contractName} - A typed contract interface for ${contractName}`);
|
|
594
|
-
if (docs && typeof docs.contract === "string" && docs.contract.trim()) {
|
|
595
|
-
contractTsLines.push(` *`);
|
|
596
|
-
for (const line of docs.contract.split(/\r?\n/g)) {
|
|
597
|
-
const t = line.trim();
|
|
598
|
-
if (!t) continue;
|
|
599
|
-
contractTsLines.push(` * ${_escapeComment(t)}`);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
contractTsLines.push(` */`);
|
|
603
|
-
contractTsLines.push(`export class ${contractName} extends Contract {`);
|
|
604
|
-
contractTsLines.push(` static readonly abi = ${JSON.stringify(abi, null, 2)} as const;`);
|
|
605
|
-
contractTsLines.push(` static readonly bytecode = ${JSON.stringify(bytecode)};`);
|
|
606
|
-
contractTsLines.push(``);
|
|
607
|
-
contractTsLines.push(` static connect(address: string, runner?: ContractRunner): ${contractName} {`);
|
|
608
|
-
contractTsLines.push(` return new ${contractName}(address, runner);`);
|
|
609
|
-
contractTsLines.push(` }`);
|
|
610
|
-
contractTsLines.push(``);
|
|
611
|
-
contractTsLines.push(` constructor(address: string, runner?: ContractRunner, _deployTx?: TransactionResponse) {`);
|
|
612
|
-
contractTsLines.push(` super(address, ${contractName}.abi as any, runner as any, ${contractName}.bytecode);`);
|
|
613
|
-
contractTsLines.push(` // @ts-expect-error internal attach`);
|
|
614
|
-
contractTsLines.push(` this._deployTx = _deployTx;`);
|
|
615
|
-
if (txFns.length) {
|
|
616
|
-
contractTsLines.push(``);
|
|
617
|
-
contractTsLines.push(` // Typed populateTransaction helpers (offline signing / sendRawTransaction flows)`);
|
|
618
|
-
contractTsLines.push(` this.populateTransaction = {`);
|
|
619
|
-
for (const fn of txFns) {
|
|
620
|
-
const name = fn.name;
|
|
621
|
-
const inputs = fn.inputs || [];
|
|
622
|
-
const argsSig = inputs
|
|
623
|
-
.map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`)
|
|
624
|
-
.join(", ");
|
|
625
|
-
const argsNames = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
|
|
626
|
-
contractTsLines.push(
|
|
627
|
-
` ${name}: async (${argsSig}${argsSig ? ", " : ""}overrides?: any): Promise<import("quantumcoin").TransactionRequest> => {`,
|
|
628
|
-
);
|
|
629
|
-
contractTsLines.push(` const data = this.interface.encodeFunctionData(${JSON.stringify(name)}, [${argsNames}]);`);
|
|
630
|
-
// Drop attacker-controllable to/data/from; protected fields win.
|
|
631
|
-
contractTsLines.push(` const safeOverrides: any = {};`);
|
|
632
|
-
contractTsLines.push(
|
|
633
|
-
` for (const k of ["value", "gasLimit", "gasPrice", "maxFeePerGas", "maxPriorityFeePerGas", "nonce", "chainId", "remarks", "signingContext"]) { if (overrides && overrides[k] !== undefined) safeOverrides[k] = overrides[k]; }`,
|
|
634
|
-
);
|
|
635
|
-
contractTsLines.push(` return { ...safeOverrides, to: this.address, data };`);
|
|
636
|
-
contractTsLines.push(` },`);
|
|
637
|
-
}
|
|
638
|
-
contractTsLines.push(` } as any;`);
|
|
639
|
-
}
|
|
640
|
-
contractTsLines.push(` }`);
|
|
641
|
-
|
|
642
|
-
for (const fn of functions) {
|
|
643
|
-
const name = fn.name;
|
|
644
|
-
const inputs = fn.inputs || [];
|
|
645
|
-
const outputs = fn.outputs || [];
|
|
646
|
-
const argsSig = inputs
|
|
647
|
-
.map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`)
|
|
648
|
-
.join(", ");
|
|
649
|
-
const argsNames = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
|
|
650
|
-
|
|
651
|
-
const mut = fn.stateMutability || "";
|
|
652
|
-
const isView = mut === "view" || mut === "pure";
|
|
653
|
-
|
|
654
|
-
let returnTs;
|
|
655
|
-
if (isView) {
|
|
656
|
-
if (outputs.length === 0) returnTs = "Promise<void>";
|
|
657
|
-
else if (outputs.length === 1) returnTs = `Promise<${_solParamToTs(outputs[0], "output", tupleReg)}>`;
|
|
658
|
-
else returnTs = `Promise<[${outputs.map((o) => _solParamToTs(o, "output", tupleReg)).join(", ")}]>`;
|
|
659
|
-
} else {
|
|
660
|
-
returnTs = "Promise<ContractTransactionResponse>";
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
contractTsLines.push(``);
|
|
664
|
-
contractTsLines.push(` /**`);
|
|
665
|
-
contractTsLines.push(` * ${name}`);
|
|
666
|
-
const fnDoc = docs && docs.functions && typeof docs.functions[name] === "string" ? docs.functions[name] : "";
|
|
667
|
-
if (fnDoc && fnDoc.trim()) {
|
|
668
|
-
contractTsLines.push(` *`);
|
|
669
|
-
for (const line of fnDoc.split(/\r?\n/g)) {
|
|
670
|
-
const t = line.trim();
|
|
671
|
-
if (!t) continue;
|
|
672
|
-
contractTsLines.push(` * ${_escapeComment(t)}`);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
contractTsLines.push(` */`);
|
|
676
|
-
if (isView) {
|
|
677
|
-
contractTsLines.push(` async ${name}(${argsSig}): ${returnTs} {`);
|
|
678
|
-
contractTsLines.push(` const res = await this.call(${JSON.stringify(name)}, [${argsNames}]);`);
|
|
679
|
-
if (outputs.length === 0) {
|
|
680
|
-
contractTsLines.push(` void res;`);
|
|
681
|
-
contractTsLines.push(` return;`);
|
|
682
|
-
} else if (outputs.length === 1) {
|
|
683
|
-
contractTsLines.push(` return (Array.isArray(res) ? res[0] : res) as unknown as ${_solParamToTs(outputs[0], "output", tupleReg)};`);
|
|
684
|
-
} else {
|
|
685
|
-
contractTsLines.push(
|
|
686
|
-
` return res as unknown as [${outputs.map((o) => _solParamToTs(o, "output", tupleReg)).join(", ")}];`,
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
contractTsLines.push(` }`);
|
|
690
|
-
} else {
|
|
691
|
-
contractTsLines.push(` async ${name}(${argsSig}${argsSig ? ", " : ""}overrides?: any): ${returnTs} {`);
|
|
692
|
-
contractTsLines.push(` return this.send(${JSON.stringify(name)}, [${argsNames}], overrides);`);
|
|
693
|
-
contractTsLines.push(` }`);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
contractTsLines.push(`}`);
|
|
698
|
-
return contractTsLines.join("\n") + "\n";
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
function _renderContractJs({ contractName, abi, bytecode, docs }) {
|
|
702
|
-
const functions = (abi || []).filter((f) => f && f.type === "function");
|
|
703
|
-
const tupleReg = _collectTupleRegistry(contractName, abi);
|
|
704
|
-
const txFns = functions.filter((f) => (f.stateMutability || "") !== "view" && (f.stateMutability || "") !== "pure");
|
|
705
|
-
|
|
706
|
-
const lines = [];
|
|
707
|
-
lines.push(`// Auto-generated by sdkgen`);
|
|
708
|
-
lines.push(`const { Contract } = require("quantumcoin");`);
|
|
709
|
-
lines.push("");
|
|
710
|
-
|
|
711
|
-
const tupleDoc = _renderTupleJsDocTypeDefs(contractName, abi, tupleReg).trimEnd();
|
|
712
|
-
if (tupleDoc) {
|
|
713
|
-
lines.push(tupleDoc);
|
|
714
|
-
lines.push("");
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
lines.push("/**");
|
|
718
|
-
lines.push(` * ${contractName} - A typed contract interface for ${contractName}`);
|
|
719
|
-
if (docs && typeof docs.contract === "string" && docs.contract.trim()) {
|
|
720
|
-
lines.push(" *");
|
|
721
|
-
for (const line of docs.contract.split(/\r?\n/g)) {
|
|
722
|
-
const t = line.trim();
|
|
723
|
-
if (!t) continue;
|
|
724
|
-
lines.push(` * ${_escapeComment(t)}`);
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
lines.push(" */");
|
|
728
|
-
lines.push(`class ${contractName} extends Contract {`);
|
|
729
|
-
lines.push(` static abi = ${JSON.stringify(abi, null, 2)};`);
|
|
730
|
-
lines.push(` static bytecode = ${JSON.stringify(bytecode)};`);
|
|
731
|
-
lines.push("");
|
|
732
|
-
lines.push(` static connect(address, runner) {`);
|
|
733
|
-
lines.push(` return new ${contractName}(address, runner);`);
|
|
734
|
-
lines.push(` }`);
|
|
735
|
-
lines.push("");
|
|
736
|
-
lines.push(` constructor(address, runner, _deployTx) {`);
|
|
737
|
-
lines.push(` super(address, ${contractName}.abi, runner, ${contractName}.bytecode);`);
|
|
738
|
-
lines.push(` this._deployTx = _deployTx;`);
|
|
739
|
-
if (txFns.length) {
|
|
740
|
-
lines.push("");
|
|
741
|
-
lines.push(" // Typed populateTransaction helpers (offline signing / sendRawTransaction flows)");
|
|
742
|
-
lines.push(" this.populateTransaction = {");
|
|
743
|
-
for (const fn of txFns) {
|
|
744
|
-
const name = fn.name;
|
|
745
|
-
const inputs = fn.inputs || [];
|
|
746
|
-
const argsNames = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
|
|
747
|
-
const argsSig = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
|
|
748
|
-
lines.push(` ${name}: async (${argsSig}${argsSig ? ", " : ""}overrides) => {`);
|
|
749
|
-
lines.push(` const data = this.interface.encodeFunctionData(${JSON.stringify(name)}, [${argsNames}]);`);
|
|
750
|
-
// Drop attacker-controllable to/data/from; protected fields win.
|
|
751
|
-
lines.push(` const safeOverrides = {};`);
|
|
752
|
-
lines.push(
|
|
753
|
-
` for (const k of ["value", "gasLimit", "gasPrice", "maxFeePerGas", "maxPriorityFeePerGas", "nonce", "chainId", "remarks", "signingContext"]) { if (overrides && overrides[k] !== undefined) safeOverrides[k] = overrides[k]; }`,
|
|
754
|
-
);
|
|
755
|
-
lines.push(` return { ...safeOverrides, to: this.address, data };`);
|
|
756
|
-
lines.push(" },");
|
|
757
|
-
}
|
|
758
|
-
lines.push(" };");
|
|
759
|
-
}
|
|
760
|
-
lines.push(` }`);
|
|
761
|
-
|
|
762
|
-
for (const fn of functions) {
|
|
763
|
-
const name = fn.name;
|
|
764
|
-
const inputs = fn.inputs || [];
|
|
765
|
-
const outputs = fn.outputs || [];
|
|
766
|
-
const argsNames = inputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
|
|
767
|
-
|
|
768
|
-
const mut = fn.stateMutability || "";
|
|
769
|
-
const isView = mut === "view" || mut === "pure";
|
|
770
|
-
|
|
771
|
-
lines.push("");
|
|
772
|
-
lines.push(" /**");
|
|
773
|
-
lines.push(` * ${name}`);
|
|
774
|
-
const fnDoc = docs && docs.functions && typeof docs.functions[name] === "string" ? docs.functions[name] : "";
|
|
775
|
-
if (fnDoc && fnDoc.trim()) {
|
|
776
|
-
lines.push(" *");
|
|
777
|
-
for (const line of fnDoc.split(/\r?\n/g)) {
|
|
778
|
-
const t = line.trim();
|
|
779
|
-
if (!t) continue;
|
|
780
|
-
lines.push(` * ${_escapeComment(t)}`);
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
for (const p of inputs) {
|
|
784
|
-
const pName = _safeIdent(p.name || "arg");
|
|
785
|
-
lines.push(` * @param {${_solParamToJsDoc(p, "input", tupleReg)}} ${pName}`);
|
|
786
|
-
}
|
|
787
|
-
if (isView) {
|
|
788
|
-
if (outputs.length === 0) {
|
|
789
|
-
lines.push(` * @returns {Promise<void>}`);
|
|
790
|
-
} else if (outputs.length === 1) {
|
|
791
|
-
lines.push(` * @returns {Promise<${_solParamToJsDoc(outputs[0], "output", tupleReg)}>} `);
|
|
792
|
-
} else {
|
|
793
|
-
lines.push(
|
|
794
|
-
` * @returns {Promise<[${outputs.map((o) => _solParamToJsDoc(o, "output", tupleReg)).join(", ")}]>}`,
|
|
795
|
-
);
|
|
796
|
-
}
|
|
797
|
-
} else {
|
|
798
|
-
lines.push(` * @returns {Promise<import("quantumcoin").ContractTransactionResponse>}`);
|
|
799
|
-
}
|
|
800
|
-
lines.push(" */");
|
|
801
|
-
|
|
802
|
-
if (isView) {
|
|
803
|
-
lines.push(` async ${name}(${argsNames}) {`);
|
|
804
|
-
lines.push(` const res = await this.call(${JSON.stringify(name)}, [${argsNames}]);`);
|
|
805
|
-
if (outputs.length === 0) {
|
|
806
|
-
lines.push(` return;`);
|
|
807
|
-
} else if (outputs.length === 1) {
|
|
808
|
-
lines.push(` return Array.isArray(res) ? res[0] : res;`);
|
|
809
|
-
} else {
|
|
810
|
-
lines.push(` return res;`);
|
|
811
|
-
}
|
|
812
|
-
lines.push(` }`);
|
|
813
|
-
} else {
|
|
814
|
-
lines.push(` async ${name}(${argsNames}${argsNames ? ", " : ""}overrides) {`);
|
|
815
|
-
lines.push(` return this.send(${JSON.stringify(name)}, [${argsNames}], overrides);`);
|
|
816
|
-
lines.push(` }`);
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
lines.push(`}`);
|
|
821
|
-
lines.push("");
|
|
822
|
-
lines.push(`module.exports = { ${contractName} };`);
|
|
823
|
-
lines.push("");
|
|
824
|
-
return lines.join("\n");
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
function _renderContractDts({ contractName, abi }) {
|
|
828
|
-
const functions = (abi || []).filter((f) => f && f.type === "function");
|
|
829
|
-
const tupleReg = _collectTupleRegistry(contractName, abi);
|
|
830
|
-
const txFns = functions.filter((f) => (f.stateMutability || "") !== "view" && (f.stateMutability || "") !== "pure");
|
|
831
|
-
const lines = [];
|
|
832
|
-
lines.push(`// Auto-generated by sdkgen`);
|
|
833
|
-
lines.push(`import { Contract, ContractRunner, ContractTransactionResponse, TransactionResponse } from "quantumcoin";`);
|
|
834
|
-
lines.push(`import type * as Types from "./types";`);
|
|
835
|
-
lines.push("");
|
|
836
|
-
const tupleDefs = _renderTupleTypeDefs(contractName, abi, tupleReg).trimEnd();
|
|
837
|
-
if (tupleDefs) {
|
|
838
|
-
lines.push(tupleDefs);
|
|
839
|
-
lines.push("");
|
|
840
|
-
}
|
|
841
|
-
lines.push(`export declare class ${contractName} extends Contract {`);
|
|
842
|
-
lines.push(` static readonly abi: readonly any[];`);
|
|
843
|
-
lines.push(` static readonly bytecode: string;`);
|
|
844
|
-
lines.push(` static connect(address: string, runner?: ContractRunner): ${contractName};`);
|
|
845
|
-
lines.push(` constructor(address: string, runner?: ContractRunner, _deployTx?: TransactionResponse);`);
|
|
846
|
-
if (txFns.length) {
|
|
847
|
-
lines.push(` readonly populateTransaction: {`);
|
|
848
|
-
for (const fn of txFns) {
|
|
849
|
-
const name = fn.name;
|
|
850
|
-
const inputs = fn.inputs || [];
|
|
851
|
-
const argsSig = inputs
|
|
852
|
-
.map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`)
|
|
853
|
-
.join(", ");
|
|
854
|
-
lines.push(
|
|
855
|
-
` ${name}(${argsSig}${argsSig ? ", " : ""}overrides?: any): Promise<import("quantumcoin").TransactionRequest>;`,
|
|
856
|
-
);
|
|
857
|
-
}
|
|
858
|
-
lines.push(` };`);
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
for (const fn of functions) {
|
|
862
|
-
const name = fn.name;
|
|
863
|
-
const inputs = fn.inputs || [];
|
|
864
|
-
const outputs = fn.outputs || [];
|
|
865
|
-
|
|
866
|
-
const argsSig = inputs.map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`).join(", ");
|
|
867
|
-
|
|
868
|
-
const mut = fn.stateMutability || "";
|
|
869
|
-
const isView = mut === "view" || mut === "pure";
|
|
870
|
-
|
|
871
|
-
let returnTs;
|
|
872
|
-
if (isView) {
|
|
873
|
-
if (outputs.length === 0) returnTs = "Promise<void>";
|
|
874
|
-
else if (outputs.length === 1) returnTs = `Promise<${_solParamToTs(outputs[0], "output", tupleReg)}>`;
|
|
875
|
-
else returnTs = `Promise<[${outputs.map((o) => _solParamToTs(o, "output", tupleReg)).join(", ")}]>`;
|
|
876
|
-
} else {
|
|
877
|
-
returnTs = "Promise<ContractTransactionResponse>";
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
if (isView) {
|
|
881
|
-
lines.push(` ${name}(${argsSig}): ${returnTs};`);
|
|
882
|
-
} else {
|
|
883
|
-
lines.push(` ${name}(${argsSig}${argsSig ? ", " : ""}overrides?: any): ${returnTs};`);
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
lines.push(`}`);
|
|
888
|
-
return lines.join("\n") + "\n";
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
function _renderFactoryTs({ contractName, abi }) {
|
|
892
|
-
const factoryName = `${contractName}__factory`;
|
|
893
|
-
const ctor = _findConstructor(abi);
|
|
894
|
-
const ctorInputs = (ctor && ctor.inputs) || [];
|
|
895
|
-
const tupleReg = _collectTupleRegistry(contractName, abi);
|
|
896
|
-
|
|
897
|
-
const factoryTsLines = [];
|
|
898
|
-
factoryTsLines.push(`// Auto-generated by sdkgen`);
|
|
899
|
-
factoryTsLines.push(`import { ContractFactory, ContractRunner, getCreateAddress } from "quantumcoin";`);
|
|
900
|
-
factoryTsLines.push(`import { ${contractName} } from "./${contractName}";`);
|
|
901
|
-
factoryTsLines.push(`import type * as Types from "./types";`);
|
|
902
|
-
|
|
903
|
-
// Import tuple input types used by the constructor (if any)
|
|
904
|
-
const ctorTupleTypes = new Set();
|
|
905
|
-
const visit = (p) => {
|
|
906
|
-
const t = String(p && p.type ? p.type : "");
|
|
907
|
-
const arr = _parseArray(t);
|
|
908
|
-
if (arr) return visit({ ...(p || {}), type: arr.inner });
|
|
909
|
-
if (t === "tuple") {
|
|
910
|
-
const key = _tupleKey(p);
|
|
911
|
-
const base = tupleReg.byKey.get(key);
|
|
912
|
-
if (base) ctorTupleTypes.add(`${base}Input`);
|
|
913
|
-
const comps = Array.isArray(p && p.components) ? p.components : [];
|
|
914
|
-
for (const c of comps) visit(c);
|
|
915
|
-
}
|
|
916
|
-
};
|
|
917
|
-
for (const p of ctorInputs) visit(p);
|
|
918
|
-
if (ctorTupleTypes.size) {
|
|
919
|
-
factoryTsLines.push(`import type { ${Array.from(ctorTupleTypes).sort().join(", ")} } from "./${contractName}";`);
|
|
920
|
-
}
|
|
921
|
-
factoryTsLines.push(``);
|
|
922
|
-
factoryTsLines.push(`export class ${factoryName} extends ContractFactory {`);
|
|
923
|
-
factoryTsLines.push(` constructor(runner: ContractRunner) {`);
|
|
924
|
-
factoryTsLines.push(` super(${contractName}.abi as any, ${contractName}.bytecode as any, runner as any);`);
|
|
925
|
-
factoryTsLines.push(` }`);
|
|
926
|
-
factoryTsLines.push(``);
|
|
927
|
-
|
|
928
|
-
// Typed deploy method (uses constructor args + optional overrides)
|
|
929
|
-
const deployArgsSig = ctorInputs
|
|
930
|
-
.map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`)
|
|
931
|
-
.join(", ");
|
|
932
|
-
const deployArgsNames = ctorInputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
|
|
933
|
-
|
|
934
|
-
factoryTsLines.push(` async deploy(${deployArgsSig}${deployArgsSig ? ", " : ""}overrides?: any): Promise<${contractName}> {`);
|
|
935
|
-
factoryTsLines.push(` const signer: any = (this as any).signer;`);
|
|
936
|
-
factoryTsLines.push(` if (!signer) { throw new Error("missing signer"); }`);
|
|
937
|
-
factoryTsLines.push(` const from: string = signer.getAddress ? await signer.getAddress() : signer.address;`);
|
|
938
|
-
factoryTsLines.push(` const provider: any = signer.provider;`);
|
|
939
|
-
factoryTsLines.push(` if (!provider || !provider.getTransactionCount) { throw new Error("missing provider"); }`);
|
|
940
|
-
factoryTsLines.push(` let nonce: number;`);
|
|
941
|
-
factoryTsLines.push(` try { nonce = await provider.getTransactionCount(from, "pending"); } catch { nonce = await provider.getTransactionCount(from, "latest"); }`);
|
|
942
|
-
factoryTsLines.push(` const address = getCreateAddress({ from, nonce });`);
|
|
943
|
-
factoryTsLines.push(` const txReq: any = this.getDeployTransaction(${deployArgsNames});`);
|
|
944
|
-
// Only allow-listed override fields may reach the signer; protected fields
|
|
945
|
-
// (to/data from txReq, and the computed nonce) always win.
|
|
946
|
-
factoryTsLines.push(` const safeOverrides: any = {};`);
|
|
947
|
-
factoryTsLines.push(
|
|
948
|
-
` for (const k of ["value", "gasLimit", "gasPrice", "maxFeePerGas", "maxPriorityFeePerGas", "remarks", "signingContext"]) { if (overrides && overrides[k] !== undefined) safeOverrides[k] = overrides[k]; }`,
|
|
949
|
-
);
|
|
950
|
-
factoryTsLines.push(` const tx = await signer.sendTransaction({ ...txReq, ...safeOverrides, nonce });`);
|
|
951
|
-
factoryTsLines.push(` return new ${contractName}(address, signer as any, tx as any);`);
|
|
952
|
-
factoryTsLines.push(` }`);
|
|
953
|
-
factoryTsLines.push(``);
|
|
954
|
-
|
|
955
|
-
factoryTsLines.push(` static connect(address: string, runner?: ContractRunner): ${contractName} {`);
|
|
956
|
-
factoryTsLines.push(` return ${contractName}.connect(address, runner);`);
|
|
957
|
-
factoryTsLines.push(` }`);
|
|
958
|
-
factoryTsLines.push(`}`);
|
|
959
|
-
|
|
960
|
-
return factoryTsLines.join("\n") + "\n";
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
function _renderFactoryJs({ contractName, abi }) {
|
|
964
|
-
const factoryName = `${contractName}__factory`;
|
|
965
|
-
const ctor = _findConstructor(abi);
|
|
966
|
-
const ctorInputs = (ctor && ctor.inputs) || [];
|
|
967
|
-
const deployArgsNames = ctorInputs.map((p, i) => _safeIdent(p.name || `arg${i}`)).join(", ");
|
|
968
|
-
|
|
969
|
-
const lines = [];
|
|
970
|
-
lines.push(`// Auto-generated by sdkgen`);
|
|
971
|
-
lines.push(`const { ContractFactory, getCreateAddress } = require("quantumcoin");`);
|
|
972
|
-
lines.push(`const { ${contractName} } = require("./${contractName}");`);
|
|
973
|
-
lines.push("");
|
|
974
|
-
lines.push(`class ${factoryName} extends ContractFactory {`);
|
|
975
|
-
lines.push(` constructor(runner) {`);
|
|
976
|
-
lines.push(` super(${contractName}.abi, ${contractName}.bytecode, runner);`);
|
|
977
|
-
lines.push(` }`);
|
|
978
|
-
lines.push("");
|
|
979
|
-
lines.push(` async deploy(${deployArgsNames}${deployArgsNames ? ", " : ""}overrides) {`);
|
|
980
|
-
lines.push(` const signer = this.signer;`);
|
|
981
|
-
lines.push(` if (!signer) { throw new Error("missing signer"); }`);
|
|
982
|
-
lines.push(` const from = signer.getAddress ? await signer.getAddress() : signer.address;`);
|
|
983
|
-
lines.push(` const provider = signer.provider;`);
|
|
984
|
-
lines.push(` if (!provider || !provider.getTransactionCount) { throw new Error("missing provider"); }`);
|
|
985
|
-
lines.push(` let nonce;`);
|
|
986
|
-
lines.push(` try { nonce = await provider.getTransactionCount(from, "pending"); } catch { nonce = await provider.getTransactionCount(from, "latest"); }`);
|
|
987
|
-
lines.push(` const address = getCreateAddress({ from, nonce });`);
|
|
988
|
-
lines.push(` const txReq = this.getDeployTransaction(${deployArgsNames});`);
|
|
989
|
-
// Only allow-listed override fields may reach the signer; protected fields
|
|
990
|
-
// (to/data from txReq, and the computed nonce) always win.
|
|
991
|
-
lines.push(` const safeOverrides = {};`);
|
|
992
|
-
lines.push(
|
|
993
|
-
` for (const k of ["value", "gasLimit", "gasPrice", "maxFeePerGas", "maxPriorityFeePerGas", "remarks", "signingContext"]) { if (overrides && overrides[k] !== undefined) safeOverrides[k] = overrides[k]; }`,
|
|
994
|
-
);
|
|
995
|
-
lines.push(` const tx = await signer.sendTransaction({ ...txReq, ...safeOverrides, nonce });`);
|
|
996
|
-
lines.push(` return new ${contractName}(address, signer, tx);`);
|
|
997
|
-
lines.push(` }`);
|
|
998
|
-
lines.push("");
|
|
999
|
-
lines.push(` static connect(address, runner) {`);
|
|
1000
|
-
lines.push(` return ${contractName}.connect(address, runner);`);
|
|
1001
|
-
lines.push(` }`);
|
|
1002
|
-
lines.push(`}`);
|
|
1003
|
-
lines.push("");
|
|
1004
|
-
lines.push(`module.exports = { ${factoryName} };`);
|
|
1005
|
-
lines.push("");
|
|
1006
|
-
return lines.join("\n");
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
function _renderFactoryDts({ contractName, abi }) {
|
|
1010
|
-
const factoryName = `${contractName}__factory`;
|
|
1011
|
-
const ctor = _findConstructor(abi);
|
|
1012
|
-
const ctorInputs = (ctor && ctor.inputs) || [];
|
|
1013
|
-
const tupleReg = _collectTupleRegistry(contractName, abi);
|
|
1014
|
-
const deployArgsSig = ctorInputs.map((p, i) => `${_safeIdent(p.name || `arg${i}`)}: ${_solParamToTs(p, "input", tupleReg)}`).join(", ");
|
|
1015
|
-
|
|
1016
|
-
const lines = [];
|
|
1017
|
-
lines.push(`// Auto-generated by sdkgen`);
|
|
1018
|
-
lines.push(`import { ContractFactory, ContractRunner } from "quantumcoin";`);
|
|
1019
|
-
lines.push(`import { ${contractName} } from "./${contractName}";`);
|
|
1020
|
-
lines.push(`import type * as Types from "./types";`);
|
|
1021
|
-
|
|
1022
|
-
const ctorTupleTypes = new Set();
|
|
1023
|
-
const visit = (p) => {
|
|
1024
|
-
const t = String(p && p.type ? p.type : "");
|
|
1025
|
-
const arr = _parseArray(t);
|
|
1026
|
-
if (arr) return visit({ ...(p || {}), type: arr.inner });
|
|
1027
|
-
if (t === "tuple") {
|
|
1028
|
-
const key = _tupleKey(p);
|
|
1029
|
-
const base = tupleReg.byKey.get(key);
|
|
1030
|
-
if (base) ctorTupleTypes.add(`${base}Input`);
|
|
1031
|
-
const comps = Array.isArray(p && p.components) ? p.components : [];
|
|
1032
|
-
for (const c of comps) visit(c);
|
|
1033
|
-
}
|
|
1034
|
-
};
|
|
1035
|
-
for (const p of ctorInputs) visit(p);
|
|
1036
|
-
if (ctorTupleTypes.size) {
|
|
1037
|
-
lines.push(`import type { ${Array.from(ctorTupleTypes).sort().join(", ")} } from "./${contractName}";`);
|
|
1038
|
-
}
|
|
1039
|
-
lines.push("");
|
|
1040
|
-
lines.push(`export declare class ${factoryName} extends ContractFactory {`);
|
|
1041
|
-
lines.push(` constructor(runner: ContractRunner);`);
|
|
1042
|
-
lines.push(` deploy(${deployArgsSig}${deployArgsSig ? ", " : ""}overrides?: any): Promise<${contractName}>;`);
|
|
1043
|
-
lines.push(` static connect(address: string, runner?: ContractRunner): ${contractName};`);
|
|
1044
|
-
lines.push(`}`);
|
|
1045
|
-
return lines.join("\n") + "\n";
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
function _renderIndexTs(contractNames) {
|
|
1049
|
-
const lines = [];
|
|
1050
|
-
lines.push(`// Auto-generated by sdkgen`);
|
|
1051
|
-
lines.push(`export * from "./types";`);
|
|
1052
|
-
for (const name of contractNames) {
|
|
1053
|
-
lines.push(`export * from "./${name}";`);
|
|
1054
|
-
lines.push(`export * from "./${name}__factory";`);
|
|
1055
|
-
}
|
|
1056
|
-
return lines.join("\n") + "\n";
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
function _renderIndexJs(contractNames) {
|
|
1060
|
-
const lines = [];
|
|
1061
|
-
lines.push(`// Auto-generated by sdkgen`);
|
|
1062
|
-
lines.push("");
|
|
1063
|
-
lines.push(`Object.assign(exports, require("./types"));`);
|
|
1064
|
-
for (const name of contractNames) {
|
|
1065
|
-
lines.push(`exports.${name} = require("./${name}").${name};`);
|
|
1066
|
-
lines.push(`exports.${name}__factory = require("./${name}__factory").${name}__factory;`);
|
|
1067
|
-
}
|
|
1068
|
-
lines.push("");
|
|
1069
|
-
return lines.join("\n");
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
function _renderIndexDts(contractNames) {
|
|
1073
|
-
const lines = [];
|
|
1074
|
-
lines.push(`// Auto-generated by sdkgen`);
|
|
1075
|
-
lines.push(`export * from "./types";`);
|
|
1076
|
-
for (const name of contractNames) {
|
|
1077
|
-
lines.push(`export * from "./${name}";`);
|
|
1078
|
-
lines.push(`export * from "./${name}__factory";`);
|
|
1079
|
-
}
|
|
1080
|
-
return lines.join("\n") + "\n";
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
/**
|
|
1084
|
-
* Generate a transactional e2e test file (JavaScript) for the typed contract package.
|
|
1085
|
-
* The test deploys the contract with constructor args (if any) and invokes one write method.
|
|
1086
|
-
*
|
|
1087
|
-
* When `bytecode` is omitted, empty, or `"0x"` the contract is treated as an interface:
|
|
1088
|
-
* the generated test still attempts the deploy (the receipt-status assertion still validates
|
|
1089
|
-
* SDK wrapper wiring) but the post-deploy `provider.getCode(...)` bytecode assertion is
|
|
1090
|
-
* skipped, since interfaces deploy with no runtime code by design.
|
|
1091
|
-
*
|
|
1092
|
-
* @param {{ contractName: string, abi: any[], bytecode?: string }} opts
|
|
1093
|
-
* @returns {string}
|
|
1094
|
-
*/
|
|
1095
|
-
function generateTransactionalTestJs(opts) {
|
|
1096
|
-
const { contractName, abi, bytecode } = opts;
|
|
1097
|
-
// This renders `${contractName}`, `${contractName}__factory` and method
|
|
1098
|
-
// names directly into JS source and a require() specifier.
|
|
1099
|
-
_assertSafeIdentifier(contractName, "contract name");
|
|
1100
|
-
_assertSafeAbiNames(abi);
|
|
1101
|
-
const isInterface = _isInterfaceBytecode(bytecode);
|
|
1102
|
-
const factoryName = `${contractName}__factory`;
|
|
1103
|
-
const ctor = _findConstructor(abi);
|
|
1104
|
-
const ctorInputs = ctor.inputs || [];
|
|
1105
|
-
|
|
1106
|
-
const ctorArgsExpr = ctorInputs.map((i) => _solTypeToTestValueExpr(i)).join(", ");
|
|
1107
|
-
|
|
1108
|
-
// ---------------------------------------------------------------------------
|
|
1109
|
-
// ERC-20 style assertions (optional)
|
|
1110
|
-
// ---------------------------------------------------------------------------
|
|
1111
|
-
const fnByName = (name) => (abi || []).find((f) => f && f.type === "function" && f.name === name);
|
|
1112
|
-
const nameFn = fnByName("name");
|
|
1113
|
-
const supplyFn = fnByName("totalSupply") || fnByName("supply");
|
|
1114
|
-
const balanceOfFn = fnByName("balanceOf");
|
|
1115
|
-
|
|
1116
|
-
const isViewNoArgs = (f) =>
|
|
1117
|
-
!!(
|
|
1118
|
-
f &&
|
|
1119
|
-
f.type === "function" &&
|
|
1120
|
-
(f.stateMutability === "view" || f.stateMutability === "pure") &&
|
|
1121
|
-
(f.inputs || []).length === 0
|
|
1122
|
-
);
|
|
1123
|
-
const isViewBalanceOf = (f) =>
|
|
1124
|
-
!!(
|
|
1125
|
-
f &&
|
|
1126
|
-
f.type === "function" &&
|
|
1127
|
-
(f.stateMutability === "view" || f.stateMutability === "pure") &&
|
|
1128
|
-
(f.inputs || []).length === 1 &&
|
|
1129
|
-
(f.inputs[0].type || "") === "address"
|
|
1130
|
-
);
|
|
1131
|
-
const isStringOut = (f) => !!(f && (f.outputs || []).length === 1 && (f.outputs[0].type || "") === "string");
|
|
1132
|
-
const isUintOut = (f) => !!(f && (f.outputs || []).length === 1 && String(f.outputs[0].type || "").startsWith("uint"));
|
|
1133
|
-
|
|
1134
|
-
const hasErc20Surface =
|
|
1135
|
-
isViewNoArgs(nameFn) && isStringOut(nameFn) && isViewNoArgs(supplyFn) && isUintOut(supplyFn) && isViewBalanceOf(balanceOfFn) && isUintOut(balanceOfFn);
|
|
1136
|
-
|
|
1137
|
-
// Common ERC-20 constructor: (string name, string symbol, uint256 initialSupply)
|
|
1138
|
-
const isErc20Ctor =
|
|
1139
|
-
ctorInputs.length === 3 &&
|
|
1140
|
-
(ctorInputs[0].type || "") === "string" &&
|
|
1141
|
-
(ctorInputs[1].type || "") === "string" &&
|
|
1142
|
-
String(ctorInputs[2].type || "").startsWith("uint");
|
|
1143
|
-
|
|
1144
|
-
const erc20TokenName = "TestToken";
|
|
1145
|
-
const erc20TokenSymbol = "TT";
|
|
1146
|
-
const erc20InitialSupply = 1000;
|
|
1147
|
-
const deployArgsExpr = hasErc20Surface && isErc20Ctor ? `${JSON.stringify(erc20TokenName)}, ${JSON.stringify(erc20TokenSymbol)}, ${erc20InitialSupply}` : ctorArgsExpr;
|
|
1148
|
-
const supplyMethodName = supplyFn && typeof supplyFn.name === "string" ? supplyFn.name : "totalSupply";
|
|
1149
|
-
const erc20Assertions =
|
|
1150
|
-
hasErc20Surface && isErc20Ctor
|
|
1151
|
-
? `// ERC-20 assertions (name / supply / balanceOf)
|
|
1152
|
-
const nm = await contract.name();
|
|
1153
|
-
assert.equal(nm, ${JSON.stringify(erc20TokenName)});
|
|
1154
|
-
|
|
1155
|
-
// Generated wrappers already unwrap single-return values to a hard type (bigint for uints).
|
|
1156
|
-
const supply = await contract.${supplyMethodName}();
|
|
1157
|
-
assert.equal(supply, ${erc20InitialSupply}n);
|
|
1158
|
-
|
|
1159
|
-
// Generated wrappers already unwrap single-return values to a hard type (bigint for uints).
|
|
1160
|
-
const bal = await contract.balanceOf(wallet.address);
|
|
1161
|
-
assert.equal(bal, ${erc20InitialSupply}n);
|
|
1162
|
-
`
|
|
1163
|
-
: "";
|
|
1164
|
-
|
|
1165
|
-
// Pick first view function with no inputs for state reads (optional).
|
|
1166
|
-
const viewNoArg = (abi || []).find(
|
|
1167
|
-
(f) => f && f.type === "function" && (f.stateMutability === "view" || f.stateMutability === "pure") && (f.inputs || []).length === 0,
|
|
1168
|
-
);
|
|
1169
|
-
|
|
1170
|
-
// Pick first state-changing function with supported params (prefer 1-arg numeric setter patterns).
|
|
1171
|
-
const writeFn =
|
|
1172
|
-
(abi || []).find((f) => {
|
|
1173
|
-
if (!f || f.type !== "function") return false;
|
|
1174
|
-
if (f.stateMutability === "view" || f.stateMutability === "pure") return false;
|
|
1175
|
-
if (!_allSupportedParams(f.inputs)) return false;
|
|
1176
|
-
return f.name === "set" && (f.inputs || []).length === 1 && (f.inputs[0].type || "").startsWith("uint");
|
|
1177
|
-
}) ||
|
|
1178
|
-
(abi || []).find(
|
|
1179
|
-
(f) => f && f.type === "function" && !(f.stateMutability === "view" || f.stateMutability === "pure") && _allSupportedParams(f.inputs),
|
|
1180
|
-
);
|
|
1181
|
-
|
|
1182
|
-
const writeName = writeFn ? writeFn.name : null;
|
|
1183
|
-
const writeArgsExpr =
|
|
1184
|
-
writeFn && (writeFn.inputs || []).length === 1 && (writeFn.inputs[0].type || "").startsWith("uint")
|
|
1185
|
-
? "456"
|
|
1186
|
-
: writeFn
|
|
1187
|
-
? (writeFn.inputs || []).map((i) => _solTypeToTestValueExpr(i)).join(", ")
|
|
1188
|
-
: "";
|
|
1189
|
-
|
|
1190
|
-
const canAssertValueChange =
|
|
1191
|
-
!!(
|
|
1192
|
-
viewNoArg &&
|
|
1193
|
-
(viewNoArg.outputs || []).length === 1 &&
|
|
1194
|
-
typeof viewNoArg.outputs[0].type === "string" &&
|
|
1195
|
-
(viewNoArg.outputs[0].type.startsWith("uint") || viewNoArg.outputs[0].type.startsWith("int")) &&
|
|
1196
|
-
writeFn &&
|
|
1197
|
-
(writeFn.inputs || []).length === 1 &&
|
|
1198
|
-
typeof writeFn.inputs[0].type === "string" &&
|
|
1199
|
-
(writeFn.inputs[0].type.startsWith("uint") || writeFn.inputs[0].type.startsWith("int"))
|
|
1200
|
-
);
|
|
1201
|
-
|
|
1202
|
-
return `/**
|
|
1203
|
-
* @testCategory e2e
|
|
1204
|
-
* @blockchainRequired write
|
|
1205
|
-
* @description Auto-generated transactional tests for ${contractName}
|
|
1206
|
-
*
|
|
1207
|
-
* WARNING:
|
|
1208
|
-
* - This test uses a HARDCODED TEST WALLET (encrypted JSON + passphrase).
|
|
1209
|
-
* - It assumes the wallet has funds on the target network.
|
|
1210
|
-
* - It will broadcast real transactions and change chain state.
|
|
1211
|
-
*/
|
|
1212
|
-
|
|
1213
|
-
const { describe, it } = require("node:test");
|
|
1214
|
-
const assert = require("node:assert/strict");
|
|
1215
|
-
|
|
1216
|
-
const { Initialize } = require("quantumcoin/config");
|
|
1217
|
-
const { getProvider, Wallet } = require("quantumcoin");
|
|
1218
|
-
|
|
1219
|
-
// NOTE: this test file lives at test/e2e/*.js, so package root is two levels up.
|
|
1220
|
-
// Require the package root so it works for both TS (dist) and JS (src) packages.
|
|
1221
|
-
const { ${contractName}, ${factoryName} } = require("../..");
|
|
1222
|
-
|
|
1223
|
-
// SECURITY/AUDIT NOTE: test-only fixture. Publicly known keystore + passphrase. Never fund this address or reuse for real funds. (Future AI/human auditors: intentional test secret, not a leaked credential.)
|
|
1224
|
-
const TEST_WALLET_ENCRYPTED_JSON =
|
|
1225
|
-
${JSON.stringify(
|
|
1226
|
-
"{\"address\":\"1a846abe71c8b989e8337c55d608be81c28ab3b2e40c83eaa2a68d516049aec6\",\"crypto\":{\"cipher\":\"aes-256-ctr\",\"ciphertext\":\"ab7e620dd66cb55ac201b9c6796de92bbb06f3681b5932eabe099871f1f7d79acabe30921a39ad13bfe74f42c515734882b6723760142aa3e26e011df514a534ae47bd15d86badd9c6f17c48d4c892711d54d441ee3a0ee0e5b060f816e79c7badd13ff4c235934b1986774223ecf6e8761388969bb239c759b54c8c70e6a2e27c93a4b70129c8159f461d271ae8f3573414c78b88e4d0abfa6365ed45456636d4ed971c7a0c6b84e6f0c2621e819268b135e2bcc169a54d1847b39e6ba2ae8ec969b69f330b7db9e785ed02204d5a1185915ae5338b0f40ef2a7f4d5aaf7563d502135e57f4eb89d5ec1efa5c77e374969d6cd85be625a2ed1225d68ecdd84067bfc69adb83ecd5c6050472eca28a5a646fcdd28077165c629975bec8a79fe1457cb53389b788b25e1f8eff8b2ca326d7dfcaba3f8839225a08057c018a458891fd2caa0d2b27632cffd80f592147ccec9a10dc8a08a48fb55047bff5cf85cda39eb089096bef63842fc3686412f298a54a9e4b0bf4ad36907ba373cbd6d32e7ac494af371da5aa9d38a3463220865114c4adc5e4ac258ba9c6af9fa2ddfd1aec2e16887e4b3977c69561df8599ac9d411c9dd2a4d57f92ea4e5c02aae3f49fb3bc83e16673e6c2dbe96bb181c8dfd0f9757ade2e4ff27215a836058c5ffeab042f6f97c7c02339f76a6284680e01b4bb733690eb3347fbfcc26614b8bf755f9dfce3fea9d4e4d15b164983201732c2e87593a86bca6da6972e128490338f76ae68135888070f4e59e90db54d23834769bdbda9769213faf5357f9167a224523975a946367b68f0cec98658575609f58bfd329e420a921c06713326e4cb20a0df1d77f37e78a320a637a96c604ca3fa89e24beb42313751b8f09b14f9c14c77e4fd13fc6382505d27c771bca0d821ec7c3765acffa99d83c50140a56b0b28101c762bd682fe55cb6f23cbeb3f421d7b36021010e45ac27160dd7ead99c864a1b550c7edb1246950fe32dcc049799f9085287f0a747a6ef7a023df46a23a22f3e833bbf8d404f84344870492658256ee1dfc40fda33bb8d48fc72d4520ba9fc820c9123104a045206809037709f2a5f6723fa77d6bac5a573823d4ec3a7f1cb786a52ee2697e622e5d75962fa554d1024a6c355e21f33a63b2b72e6c4742a8b1c373aa532b40518c38c90b5373c2eb8c9d7be2a9e16047a3ee09dc9a6849deac5183ace6cfe91a9bef2ffc0a7df6ccebfd4c858c84b0e0355650d7466971e66f1e3883013e5ad1be33199b1d110b79070ac1b745ccb14cf63a08f8cca3a21c9525e626ff5f0c34746e10750fb742ad51f11f2acae3676c2111853d7250d01b77821a6ba9e04400ba2c543ca9f2d701ae6f47bfad14ffe3039ee9e71f7b2401359ade9938750ddb9c5a8b018a7929ed8d0e717ff1861446ce17535e9b17c187711190aae3388bd9490837a636c25ed4d42d7079ad1a51e13292c683d5d012abcf46965c534b83ab53f2c1f0cf5830ef7582e06863a33c19a70511df632885d63245965047ea96b56f1af5b3b94a54999f784fb9574fdfcd7c1230e07a2aaa04acd3097b2b9f8ddba05ae9734491deb5c1a513c76ed276cb78bbf4839dae3156d76af444a5805129d5df791167a9c8576a1d7f760b2d2797c4658669608706fbd0ace1be2346f74862dfc9ef518e55632e43c043186e5d070deb34d12fb9e5aba84e5cb50213dc88efd39cc35bf42455aa82d5e3b707b3140be3b8623b34fdd81d08615c188ae8438a13881fdf6bf32f2cb9ff5fa625561040c6b71d4b8eccc90bc3b99650d28dd1ee63773e49664e3d48c484996b290943635a6f2eb1ce9796d3fa144a3f00ef82faaa32d6a413668f7b521517cb68b2b017fcf56c79326fa5e4060e643631ca3f0a0dc0ed718798b6f46b130d437c33f64039e887324b6f5e604b1669d613923794edbf04b1b3caea54793b52b44b170173a4f25c7ecef3b71e2aad76e556b1cb9f1d637ec52ececfa950dd31dbb6a60828a3ad34c1beffe09eb4785786d63bad10a0b0f66ea88c57380f38ea85f018dbd7f538cf1ee7624095b9a01ec5edd528f281168af020609e651ff316aa1320a710134ddfca600cc72174dcdb846d2aa29916488aa1b537b66da92e61af526debef4eb38c984569eaf549ff2129449269b492d030cd74d885f6f5785881cc4804b4a8a09ba4ff7aefe9074ac7d0c4f05d51fe4cc0ff7388a772092b9d02d70e5433a5cf3e02f46a6bd6b818d59a07ce3b9fbbf8b5faba74563bcc5240930c2d406c9aaee3e3ce0429bf68ac2b0a57adb09414cff50817d2a48fb9fa624ab863cb0c31a8b8dc5eaf6fa68cc1d7c6c685c5a33edd5c8933b9e8ab628ee428d0743699b2ff17f25586c7ce959280bb0b8c5342251f0a30b53dbc7bf1ee426ac9619c3560f811f2268ee37f189794e2e4b3db3a2fb2e34b649e504fb467438abfd1082619cc4a0b30d66beb831077812e418d2e2148db10cf4d4a29101ca52ec445b8d83519dd7de85a98e0beae9ee537096d3f1a55a7a80cdfa93d25f07c9f98e8af18cde19ec1f99c5dd4588b717a5039ddb7f177717caf0d0fd45420a70dbd6d3146890d9e450d5224146db4c33b779e3c3a04b976c052bad042ac57dd38be45407808c0fb0d7e2a8819e6cd53c6739e6612996ddaa6f066552590aa0343bc1e62b298ff2514a0cef8be21956c2e942816f7a3a3a0935eaf9b37251409ce444c986c3817e82835555fe18239f3ae33469d7965c2bde9991fde556bd07af01df52bbde0c35bb4ef48e3b5d0db53f8ca4ed35b83f760f0a1bc4ed9f86e85d6039a17df373c85402ef956f01db00eb39c4b74bd0660d29ee746714d9780d738e05c6cca414ce3d7b40dda8036a9eea9ab1388805f913eb19bdd3f09d9e161eaa50231bd9caba61971f194332dd28c696a60458c1c6c2cc5da8b1192611c7c553e9e12fe48ce46bbb891be8bb118721c86222e671ddd1da8f0ccb2b68e02f2014b4925e904e88369aaf7466bd7033a60c265d45955944916ecbdb84bf1b522b01b0149c632e04c568a7eb627c5bb90ece052ebcf79166c28b30d23fe52da0a5ab5dea83ca479a3e3b7a9cfbbfea04dbe6137c19d067317c2ec427a8c75a6b06bec6dcd5d5c0edc9aa80b9003b8e17c088b2f3db327d3e42630d82d20120240c3ba56232280787da4aabbf5bc95a864029f00710e195f2a76460a0317d10b552fe1bea097e41d49756c680a41d6ac186e62169b6b6cd7776ea84618b5b752328a5bacaa10aa122ff9b2698b43efe73d852a899db644863c8c9bc8068ea86ea843fd6fe36272b91cdc5d5317083ef3fd1e5462a0b0d0604dc57b3bbfceb0fca4cd349625dd7b25166af30efe5ee6a0af953a74d65f4736c59918ee55a3b0d9d9d42e04c7f8a77e479109f740e20c464d5d7e3d16805f47b61f403ff7f408c9e850d9baacd8067e544536a4953480b0f9ee9cd45f41ebd67b51f78788a6470cb1e5ca72ca346ce8a50d0ca0c921d5576a4455a1afb6d0bc688004712ee122cacdb29c51e84893324c27fa4a3f1917edf5352272b4c97579a6152e4b77663d0ab532915f2eeb6a862de8b696452321b660c3f2449673d086e95a7af28845a5259b763e0fcd09f72acf7b6c811066263060e5aa5b24658e880a01fd56bda4dad5ab604e129290f7d5489728f2a40968c6168b21cebbbcd11727cc9e9160c4e92e04387d3b0d62aab06a61f26daedd9fed11816ef2180172a47f47184ac4032b88758c98a2e0fb200f70e93ba695f5ebb7a1029610ad360d3b7fa1b4640b9dc674d3625eef786da93dff19bc7991b5d6193a3896664763fde479b5dfc04812111a80782854f2cf68ca7d82765cc9eb40fba4b44640710ed6e653abf9f07b466333f4fd22784d53cf40e17120f42caa841eaa24056b237827b0f47f7257c103c35027e9f503e5acfd023e7357b600d3084d361d5ee65ba319b45c153212a54e6fed85af7e43e0a926ebcbc2edf8de7e2ec9528f00bec262ad04d5c9dafccaea06a24748d28bf1799bae0e895543084539c50b5aaa4fb50d7431d6f0c8cee2a54aaf7ee7919b55bf40adb688632e5dbe273cea09e97b19c3d8e1f4de000deb66fa1942ad03a62d3252f51992244366c156000b49c297167a6cbdedea7ebae139d295f0ad298e0864249b905b7eb812886ec70ecdb286702274b5b8574149bf3866f9e46b997ff5ed622b169a0eb071347f18d530db1663906a28f4544ee4e004ab87b65476af30ede118052ff052b8dc986ca2c93dd5d4943266a579c7698ea014f688b3e8063a107feb162d392e2177b01bff77fb5abe5feebd0607158049a5a093325b7c9ee6b4dfa7a9f65c7c2fb628920d3603a1c2dad979eaa047cd661a268af1078c9788d720e64e4ce9d12e68de1e417ef2f293323681e1071f9220e1ee43d2e29d111b870ce3439f5100ecd4551ab65ee74aa1667e564957e9bc0ae1ea193980da2a0ec2698073388c85bec25ef447f0d5e93a5203fa44dff268e5cb799ed3b66e63d5e07b487e7534f24934c73a62a243e0151843a0fd3807711a101eaa7fc71f0ba68aebb9534d57cba41b094eebfb4c31cca8eddfa426f676aa347be8a7023a4e91ddb154b35cd4d5f7dbc2e5db491de99f33fc2cff2d57029ac950e1ccd681980af6a4e8969dfe39b3c7bfcbcf8fac92f1e6ec9fe572bfa6a7d65860eab2ed10ac01a71290b52e3148e84b7376a8605cd2bb0e8681ffc54691ce087685e33921bd44d36c78291713dce17569570f62137e6904f0d68cf53aa2ec395c389a75141f08114fb293ea63950e4ffee55ec6fc83cf44876b8e7f25cdd393ff87b9eda6eb746085b61a6900de191f0ce2cb388d61ece52e78bc47368194e8e00277e0d1631e6b9d4626ef76f8522582ccd5a40be3febc699bb510acc6271d55ff0f4cf3bb7669855a72efd9ca3e1056a2fe592a5bc877cce2b1f63b58383971da87873d2d1349cf5881242cdce4e7e2c5c514755746a0e0a7c2a6d9701cde005ae3420beb17c379a3516662253554f51f0423bb1844b0b90c54ed8177ceb0e1036a6609d836e748ca06c40ca64befadc6443ec286a0ce464678e8d11eb455f7bb305acebf6cb1f50e394a9bfeb752df1687831bac9cdd811f4f112ef6658d0f8799a866374ff96c5e2b79f30e7a74f8a2bc9ed1f88f01f30e30cb78ffb2bff10108f35e910ee3be4463e9e6f0ed910e8d598326e71dfa2277ffe5579d7fe9b6018bfe295b25219eae07b3b0270665c3fa00c3e0d180812b5cd62925585de84a7c48a9a86dba96544a251654d1966e082432dc85b6149cf21e91a46020ec32b66d28ba3b6a90c0617bc6fdd55aea819af2bcf84864ad60c28fe3c9f8339d0aee68b39d97f63b6e082835d86119cf9b9fdc8b827c847ce40aa10e1577a710132316845e825345e95bdf94d0c66ec65a6c4319fce4792313663b5f7a651a6710783e6ab71608ac6cbbf3af6911adf596ccf7c172b9bd5bceb6db379967b32b143bdd11d2ee12ddf64ecef6391e0f8570e6cddd3db95204919362b89b739fa94e7c1bfde799fd5e22aa25ca6ca42e30c08e23aae2385d99ebab441072a880dcefdab74a4c9bd39d363f6d1933d59400fca161d432aa00f23b1b1c19a154be8989699d549b66d44e39896f5523443bc6ddf4a65e91f1f3fb7b52318869a05856a4fc92f3694c81ed833c972fb918f7e5\",\"cipherparams\":{\"iv\":\"8c46d6162cd4c765759aedcbce2a5874\"},\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"n\":262144,\"p\":1,\"r\":8,\"salt\":\"82fb6cdc6917609135277badacf15baa31899d08b71a5a0fa33167167c161537\"},\"mac\":\"9187b17f7eca48e6b8c586b0cd790dbe0feb876ac8385f93faa7d5e22a3c8fc7\"},\"id\":\"92caf6ee-2d43-48c0-859e-ffa1e0e23312\",\"version\":3}",
|
|
1227
|
-
)};
|
|
1228
|
-
const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
|
|
1229
|
-
|
|
1230
|
-
describe("${contractName} transactional", () => {
|
|
1231
|
-
it("deploys and invokes contract", async (t) => {
|
|
1232
|
-
const rpcUrl = process.env.QC_RPC_URL;
|
|
1233
|
-
if (!rpcUrl) {
|
|
1234
|
-
t.skip("QC_RPC_URL not provided");
|
|
1235
|
-
return;
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
|
|
1239
|
-
await Initialize(null);
|
|
1240
|
-
|
|
1241
|
-
const provider = getProvider(rpcUrl, chainId);
|
|
1242
|
-
const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
|
|
1243
|
-
|
|
1244
|
-
const factory = new ${factoryName}(wallet);
|
|
1245
|
-
// Build deploy transaction + estimate gas (best-effort).
|
|
1246
|
-
const deployTxReq = factory.getDeployTransaction(${deployArgsExpr});
|
|
1247
|
-
let deployGasLimit = 600000;
|
|
1248
|
-
try {
|
|
1249
|
-
const est = await provider.estimateGas({ from: wallet.address, data: deployTxReq.data });
|
|
1250
|
-
deployGasLimit = Number(est + 200_000n);
|
|
1251
|
-
} catch {
|
|
1252
|
-
// Keep fallback. Some RPCs do not support estimateGas for create.
|
|
1253
|
-
deployGasLimit = 6_000_000;
|
|
1254
|
-
}
|
|
1255
|
-
// Some large contracts underestimate gas; apply a sane floor based on bytecode size.
|
|
1256
|
-
const bytecodeSize = (${contractName}.bytecode || "").length;
|
|
1257
|
-
if (bytecodeSize > 20000 && deployGasLimit < 6_000_000) deployGasLimit = 6_000_000;
|
|
1258
|
-
|
|
1259
|
-
const contract = await factory.deploy(${deployArgsExpr}${deployArgsExpr ? ", " : ""}{ gasLimit: deployGasLimit });
|
|
1260
|
-
|
|
1261
|
-
const deployTx = contract.deployTransaction();
|
|
1262
|
-
assert.ok(deployTx && deployTx.hash);
|
|
1263
|
-
const deployReceipt = await deployTx.wait(1, 600_000);
|
|
1264
|
-
assert.ok(deployReceipt);
|
|
1265
|
-
assert.ok(deployReceipt.blockNumber != null);
|
|
1266
|
-
|
|
1267
|
-
${isInterface
|
|
1268
|
-
? `// Skipping bytecode check: ${contractName} is an interface (no bytecode).`
|
|
1269
|
-
: `const code = await provider.getCode(contract.target, "latest");
|
|
1270
|
-
assert.ok(code && code !== "0x");`}
|
|
1271
|
-
|
|
1272
|
-
${erc20Assertions ? erc20Assertions : `// (no ERC-20 surface detected for extra assertions)`}
|
|
1273
|
-
|
|
1274
|
-
${viewNoArg ? `// Basic view call
|
|
1275
|
-
const before = await contract.${viewNoArg.name}();
|
|
1276
|
-
void before;` : `// No zero-arg view method detected; deployment is still validated.`}
|
|
1277
|
-
|
|
1278
|
-
${writeName ? `// Write call + wait
|
|
1279
|
-
const tx = await contract.${writeName}(${writeArgsExpr}${writeArgsExpr ? ", " : ""}{ gasLimit: 200000 });
|
|
1280
|
-
await tx.wait(1, 600_000);` : `// No supported write method detected for auto invocation.`}
|
|
1281
|
-
|
|
1282
|
-
${canAssertValueChange ? `// Assert value change (best-effort; normalize to BigInt)
|
|
1283
|
-
const after = await contract.${viewNoArg.name}();
|
|
1284
|
-
assert.equal(after, 456n);` : `// No compatible getter+setter pair detected for value assertions.`}
|
|
1285
|
-
}, { timeout: 900_000 });
|
|
1286
|
-
});
|
|
1287
|
-
`;
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
/**
|
|
1291
|
-
* For a single contract ABI, compute deploy args and optional view/write method for e2e.
|
|
1292
|
-
* @param {{ contractName: string, abi: any[] }} opts
|
|
1293
|
-
* @returns {{ ctorArgsExpr: string, deployArgsExpr: string, viewName: string | null, writeName: string | null, writeArgsExpr: string }}
|
|
1294
|
-
*/
|
|
1295
|
-
function _getContractTestMeta(opts) {
|
|
1296
|
-
const { contractName, abi } = opts;
|
|
1297
|
-
const ctor = _findConstructor(abi);
|
|
1298
|
-
const ctorInputs = ctor.inputs || [];
|
|
1299
|
-
const ctorArgsExpr = ctorInputs.map((i) => _solTypeToTestValueExpr(i)).join(", ");
|
|
1300
|
-
const viewNoArg = (abi || []).find(
|
|
1301
|
-
(f) => f && f.type === "function" && (f.stateMutability === "view" || f.stateMutability === "pure") && (f.inputs || []).length === 0,
|
|
1302
|
-
);
|
|
1303
|
-
const writeFn =
|
|
1304
|
-
(abi || []).find(
|
|
1305
|
-
(f) =>
|
|
1306
|
-
f && f.type === "function" && !(f.stateMutability === "view" || f.stateMutability === "pure") && f.name === "set" &&
|
|
1307
|
-
(f.inputs || []).length === 1 &&
|
|
1308
|
-
(f.inputs[0].type || "").startsWith("uint") &&
|
|
1309
|
-
_allSupportedParams(f.inputs),
|
|
1310
|
-
) ||
|
|
1311
|
-
(abi || []).find(
|
|
1312
|
-
(f) => f && f.type === "function" && !(f.stateMutability === "view" || f.stateMutability === "pure") && _allSupportedParams(f.inputs),
|
|
1313
|
-
);
|
|
1314
|
-
const writeArgsExpr =
|
|
1315
|
-
writeFn && (writeFn.inputs || []).length === 1 && (writeFn.inputs[0].type || "").startsWith("uint")
|
|
1316
|
-
? "456"
|
|
1317
|
-
: writeFn
|
|
1318
|
-
? (writeFn.inputs || []).map((i) => _solTypeToTestValueExpr(i)).join(", ")
|
|
1319
|
-
: "";
|
|
1320
|
-
return {
|
|
1321
|
-
ctorArgsExpr,
|
|
1322
|
-
deployArgsExpr: ctorArgsExpr,
|
|
1323
|
-
viewName: viewNoArg ? viewNoArg.name : null,
|
|
1324
|
-
writeName: writeFn ? writeFn.name : null,
|
|
1325
|
-
writeArgsExpr,
|
|
1326
|
-
};
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
/**
|
|
1330
|
-
* Generate a single transactional e2e test that deploys and invokes methods on ALL contracts.
|
|
1331
|
-
* Used when the package has multiple contracts so one test exercises every contract.
|
|
1332
|
-
*
|
|
1333
|
-
* Accepts `bytecode` per artifact for forward-compatibility, although the multi-contract
|
|
1334
|
-
* template currently emits no `getCode` assertion (so the value is not yet consumed).
|
|
1335
|
-
*
|
|
1336
|
-
* @param {{ artifacts: Array<{ contractName: string, abi: any[], bytecode?: string }> }} opts
|
|
1337
|
-
* @returns {string}
|
|
1338
|
-
*/
|
|
1339
|
-
function generateAllContractsTransactionalTestJs(opts) {
|
|
1340
|
-
const { artifacts } = opts;
|
|
1341
|
-
if (!artifacts || artifacts.length < 2) {
|
|
1342
|
-
throw new Error("generateAllContractsTransactionalTestJs requires at least 2 artifacts");
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
// Contract names and method names are interpolated into source + require().
|
|
1346
|
-
for (const a of artifacts) {
|
|
1347
|
-
_assertSafeIdentifier(a && a.contractName, "contract name");
|
|
1348
|
-
_assertSafeAbiNames(a && a.abi);
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
const requireNames = [];
|
|
1352
|
-
for (const a of artifacts) {
|
|
1353
|
-
requireNames.push(a.contractName, `${a.contractName}__factory`);
|
|
1354
|
-
}
|
|
1355
|
-
const requireLine = `const { ${requireNames.join(", ")} } = require("../..");`;
|
|
1356
|
-
|
|
1357
|
-
const blocks = [];
|
|
1358
|
-
for (const a of artifacts) {
|
|
1359
|
-
const meta = _getContractTestMeta({ contractName: a.contractName, abi: a.abi });
|
|
1360
|
-
const factoryName = `${a.contractName}__factory`;
|
|
1361
|
-
const deployRhs =
|
|
1362
|
-
meta.ctorArgsExpr
|
|
1363
|
-
? `await (new ${factoryName}(wallet)).deploy(${meta.deployArgsExpr}, { gasLimit: deployGasLimit })`
|
|
1364
|
-
: `await (new ${factoryName}(wallet)).deploy({ gasLimit: deployGasLimit })`;
|
|
1365
|
-
let invokeBlock = "";
|
|
1366
|
-
if (meta.viewName) {
|
|
1367
|
-
invokeBlock += `const _v = await ${a.contractName}Inst.${meta.viewName}();\n void _v;\n`;
|
|
1368
|
-
}
|
|
1369
|
-
if (meta.writeName) {
|
|
1370
|
-
invokeBlock += `const _tx = await ${a.contractName}Inst.${meta.writeName}(${meta.writeArgsExpr}${meta.writeArgsExpr ? ", " : ""}{ gasLimit: 200000 });\n await _tx.wait(1, 600_000);\n`;
|
|
1371
|
-
}
|
|
1372
|
-
blocks.push({
|
|
1373
|
-
contractName: a.contractName,
|
|
1374
|
-
factoryName,
|
|
1375
|
-
deployRhs,
|
|
1376
|
-
invokeBlock: invokeBlock || `// No view/write method detected for ${a.contractName}\n`,
|
|
1377
|
-
});
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
const deployAndInvokeBlocks = blocks
|
|
1381
|
-
.map(
|
|
1382
|
-
(b) => `
|
|
1383
|
-
let ${b.contractName}Inst = ${b.deployRhs};
|
|
1384
|
-
const _deployTx${b.contractName} = ${b.contractName}Inst.deployTransaction();
|
|
1385
|
-
assert.ok(_deployTx${b.contractName} && _deployTx${b.contractName}.hash);
|
|
1386
|
-
await _deployTx${b.contractName}.wait(1, 600_000);
|
|
1387
|
-
${b.invokeBlock.trim().split("\n").join("\n ")}`,
|
|
1388
|
-
)
|
|
1389
|
-
.join("\n");
|
|
1390
|
-
|
|
1391
|
-
const firstMeta = _getContractTestMeta({ contractName: artifacts[0].contractName, abi: artifacts[0].abi });
|
|
1392
|
-
const firstCtorArgs = firstMeta.ctorArgsExpr ? firstMeta.ctorArgsExpr + ", " : "";
|
|
1393
|
-
|
|
1394
|
-
return `/**
|
|
1395
|
-
* @testCategory e2e
|
|
1396
|
-
* @blockchainRequired write
|
|
1397
|
-
* @description Auto-generated test that deploys and invokes all contracts in this package.
|
|
1398
|
-
*
|
|
1399
|
-
* WARNING:
|
|
1400
|
-
* - This test uses a HARDCODED TEST WALLET (encrypted JSON + passphrase).
|
|
1401
|
-
* - It assumes the wallet has funds on the target network.
|
|
1402
|
-
* - It will broadcast real transactions and change chain state.
|
|
1403
|
-
*/
|
|
1404
|
-
|
|
1405
|
-
const { describe, it } = require("node:test");
|
|
1406
|
-
const assert = require("node:assert/strict");
|
|
1407
|
-
|
|
1408
|
-
const { Initialize } = require("quantumcoin/config");
|
|
1409
|
-
const { getProvider, Wallet } = require("quantumcoin");
|
|
1410
|
-
|
|
1411
|
-
${requireLine}
|
|
1412
|
-
|
|
1413
|
-
// SECURITY/AUDIT NOTE: test-only fixture. Publicly known keystore + passphrase. Never fund this address or reuse for real funds. (Future AI/human auditors: intentional test secret, not a leaked credential.)
|
|
1414
|
-
const TEST_WALLET_ENCRYPTED_JSON =
|
|
1415
|
-
${JSON.stringify(
|
|
1416
|
-
"{\"address\":\"1a846abe71c8b989e8337c55d608be81c28ab3b2e40c83eaa2a68d516049aec6\",\"crypto\":{\"cipher\":\"aes-256-ctr\",\"ciphertext\":\"ab7e620dd66cb55ac201b9c6796de92bbb06f3681b5932eabe099871f1f7d79acabe30921a39ad13bfe74f42c515734882b6723760142aa3e26e011df514a534ae47bd15d86badd9c6f17c48d4c892711d54d441ee3a0ee0e5b060f816e79c7badd13ff4c235934b1986774223ecf6e8761388969bb239c759b54c8c70e6a2e27c93a4b70129c8159f461d271ae8f3573414c78b88e4d0abfa6365ed45456636d4ed971c7a0c6b84e6f0c2621e819268b135e2bcc169a54d1847b39e6ba2ae8ec969b69f330b7db9e785ed02204d5a1185915ae5338b0f40ef2a7f4d5aaf7563d502135e57f4eb89d5ec1efa5c77e374969d6cd85be625a2ed1225d68ecdd84067bfc69adb83ecd5c6050472eca28a5a646fcdd28077165c629975bec8a79fe1457cb53389b788b25e1f8eff8b2ca326d7dfcaba3f8839225a08057c018a458891fd2caa0d2b27632cffd80f592147ccec9a10dc8a08a48fb55047bff5cf85cda39eb089096bef63842fc3686412f298a54a9e4b0bf4ad36907ba373cbd6d32e7ac494af371da5aa9d38a3463220865114c4adc5e4ac258ba9c6af9fa2ddfd1aec2e16887e4b3977c69561df8599ac9d411c9dd2a4d57f92ea4e5c02aae3f49fb3bc83e16673e6c2dbe96bb181c8dfd0f9757ade2e4ff27215a836058c5ffeab042f6f97c7c02339f76a6284680e01b4bb733690eb3347fbfcc26614b8bf755f9dfce3fea9d4e4d15b164983201732c2e87593a86bca6da6972e128490338f76ae68135888070f4e59e90db54d23834769bdbda9769213faf5357f9167a224523975a946367b68f0cec98658575609f58bfd329e420a921c06713326e4cb20a0df1d77f37e78a320a637a96c604ca3fa89e24beb42313751b8f09b14f9c14c77e4fd13fc6382505d27c771bca0d821ec7c3765acffa99d83c50140a56b0b28101c762bd682fe55cb6f23cbeb3f421d7b36021010e45ac27160dd7ead99c864a1b550c7edb1246950fe32dcc049799f9085287f0a747a6ef7a023df46a23a22f3e833bbf8d404f84344870492658256ee1dfc40fda33bb8d48fc72d4520ba9fc820c9123104a045206809037709f2a5f6723fa77d6bac5a573823d4ec3a7f1cb786a52ee2697e622e5d75962fa554d1024a6c355e21f33a63b2b72e6c4742a8b1c373aa532b40518c38c90b5373c2eb8c9d7be2a9e16047a3ee09dc9a6849deac5183ace6cfe91a9bef2ffc0a7df6ccebfd4c858c84b0e0355650d7466971e66f1e3883013e5ad1be33199b1d110b79070ac1b745ccb14cf63a08f8cca3a21c9525e626ff5f0c34746e10750fb742ad51f11f2acae3676c2111853d7250d01b77821a6ba9e04400ba2c543ca9f2d701ae6f47bfad14ffe3039ee9e71f7b2401359ade9938750ddb9c5a8b018a7929ed8d0e717ff1861446ce17535e9b17c187711190aae3388bd9490837a636c25ed4d42d7079ad1a51e13292c683d5d012abcf46965c534b83ab53f2c1f0cf5830ef7582e06863a33c19a70511df632885d63245965047ea96b56f1af5b3b94a54999f784fb9574fdfcd7c1230e07a2aaa04acd3097b2b9f8ddba05ae9734491deb5c1a513c76ed276cb78bbf4839dae3156d76af444a5805129d5df791167a9c8576a1d7f760b2d2797c4658669608706fbd0ace1be2346f74862dfc9ef518e55632e43c043186e5d070deb34d12fb9e5aba84e5cb50213dc88efd39cc35bf42455aa82d5e3b707b3140be3b8623b34fdd81d08615c188ae8438a13881fdf6bf32f2cb9ff5fa625561040c6b71d4b8eccc90bc3b99650d28dd1ee63773e49664e3d48c484996b290943635a6f2eb1ce9796d3fa144a3f00ef82faaa32d6a413668f7b521517cb68b2b017fcf56c79326fa5e4060e643631ca3f0a0dc0ed718798b6f46b130d437c33f64039e887324b6f5e604b1669d613923794edbf04b1b3caea54793b52b44b170173a4f25c7ecef3b71e2aad76e556b1cb9f1d637ec52ececfa950dd31dbb6a60828a3ad34c1beffe09eb4785786d63bad10a0b0f66ea88c57380f38ea85f018dbd7f538cf1ee7624095b9a01ec5edd528f281168af020609e651ff316aa1320a710134ddfca600cc72174dcdb846d2aa29916488aa1b537b66da92e61af526debef4eb38c984569eaf549ff2129449269b492d030cd74d885f6f5785881cc4804b4a8a09ba4ff7aefe9074ac7d0c4f05d51fe4cc0ff7388a772092b9d02d70e5433a5cf3e02f46a6bd6b818d59a07ce3b9fbbf8b5faba74563bcc5240930c2d406c9aaee3e3ce0429bf68ac2b0a57adb09414cff50817d2a48fb9fa624ab863cb0c31a8b8dc5eaf6fa68cc1d7c6c685c5a33edd5c8933b9e8ab628ee428d0743699b2ff17f25586c7ce959280bb0b8c5342251f0a30b53dbc7bf1ee426ac9619c3560f811f2268ee37f189794e2e4b3db3a2fb2e34b649e504fb467438abfd1082619cc4a0b30d66beb831077812e418d2e2148db10cf4d4a29101ca52ec445b8d83519dd7de85a98e0beae9ee537096d3f1a55a7a80cdfa93d25f07c9f98e8af18cde19ec1f99c5dd4588b717a5039ddb7f177717caf0d0fd45420a70dbd6d3146890d9e450d5224146db4c33b779e3c3a04b976c052bad042ac57dd38be45407808c0fb0d7e2a8819e6cd53c6739e6612996ddaa6f066552590aa0343bc1e62b298ff2514a0cef8be21956c2e942816f7a3a3a0935eaf9b37251409ce444c986c3817e82835555fe18239f3ae33469d7965c2bde9991fde556bd07af01df52bbde0c35bb4ef48e3b5d0db53f8ca4ed35b83f760f0a1bc4ed9f86e85d6039a17df373c85402ef956f01db00eb39c4b74bd0660d29ee746714d9780d738e05c6cca414ce3d7b40dda8036a9eea9ab1388805f913eb19bdd3f09d9e161eaa50231bd9caba61971f194332dd28c696a60458c1c6c2cc5da8b1192611c7c553e9e12fe48ce46bbb891be8bb118721c86222e671ddd1da8f0ccb2b68e02f2014b4925e904e88369aaf7466bd7033a60c265d45955944916ecbdb84bf1b522b01b0149c632e04c568a7eb627c5bb90ece052ebcf79166c28b30d23fe52da0a5ab5dea83ca479a3e3b7a9cfbbfea04dbe6137c19d067317c2ec427a8c75a6b06bec6dcd5d5c0edc9aa80b9003b8e17c088b2f3db327d3e42630d82d20120240c3ba56232280787da4aabbf5bc95a864029f00710e195f2a76460a0317d10b552fe1bea097e41d49756c680a41d6ac186e62169b6b6cd7776ea84618b5b752328a5bacaa10aa122ff9b2698b43efe73d852a899db644863c8c9bc8068ea86ea843fd6fe36272b91cdc5d5317083ef3fd1e5462a0b0d0604dc57b3bbfceb0fca4cd349625dd7b25166af30efe5ee6a0af953a74d65f4736c59918ee55a3b0d9d9d42e04c7f8a77e479109f740e20c464d5d7e3d16805f47b61f403ff7f408c9e850d9baacd8067e544536a4953480b0f9ee9cd45f41ebd67b51f78788a6470cb1e5ca72ca346ce8a50d0ca0c921d5576a4455a1afb6d0bc688004712ee122cacdb29c51e84893324c27fa4a3f1917edf5352272b4c97579a6152e4b77663d0ab532915f2eeb6a862de8b696452321b660c3f2449673d086e95a7af28845a5259b763e0fcd09f72acf7b6c811066263060e5aa5b24658e880a01fd56bda4dad5ab604e129290f7d5489728f2a40968c6168b21cebbbcd11727cc9e9160c4e92e04387d3b0d62aab06a61f26daedd9fed11816ef2180172a47f47184ac4032b88758c98a2e0fb200f70e93ba695f5ebb7a1029610ad360d3b7fa1b4640b9dc674d3625eef786da93dff19bc7991b5d6193a3896664763fde479b5dfc04812111a80782854f2cf68ca7d82765cc9eb40fba4b44640710ed6e653abf9f07b466333f4fd22784d53cf40e17120f42caa841eaa24056b237827b0f47f7257c103c35027e9f503e5acfd023e7357b600d3084d361d5ee65ba319b45c153212a54e6fed85af7e43e0a926ebcbc2edf8de7e2ec9528f00bec262ad04d5c9dafccaea06a24748d28bf1799bae0e895543084539c50b5aaa4fb50d7431d6f0c8cee2a54aaf7ee7919b55bf40adb688632e5dbe273cea09e97b19c3d8e1f4de000deb66fa1942ad03a62d3252f51992244366c156000b49c297167a6cbdedea7ebae139d295f0ad298e0864249b905b7eb812886ec70ecdb286702274b5b8574149bf3866f9e46b997ff5ed622b169a0eb071347f18d530db1663906a28f4544ee4e004ab87b65476af30ede118052ff052b8dc986ca2c93dd5d4943266a579c7698ea014f688b3e8063a107feb162d392e2177b01bff77fb5abe5feebd0607158049a5a093325b7c9ee6b4dfa7a9f65c7c2fb628920d3603a1c2dad979eaa047cd661a268af1078c9788d720e64e4ce9d12e68de1e417ef2f293323681e1071f9220e1ee43d2e29d111b870ce3439f5100ecd4551ab65ee74aa1667e564957e9bc0ae1ea193980da2a0ec2698073388c85bec25ef447f0d5e93a5203fa44dff268e5cb799ed3b66e63d5e07b487e7534f24934c73a62a243e0151843a0fd3807711a101eaa7fc71f0ba68aebb9534d57cba41b094eebfb4c31cca8eddfa426f676aa347be8a7023a4e91ddb154b35cd4d5f7dbc2e5db491de99f33fc2cff2d57029ac950e1ccd681980af6a4e8969dfe39b3c7bfcbcf8fac92f1e6ec9fe572bfa6a7d65860eab2ed10ac01a71290b52e3148e84b7376a8605cd2bb0e8681ffc54691ce087685e33921bd44d36c78291713dce17569570f62137e6904f0d68cf53aa2ec395c389a75141f08114fb293ea63950e4ffee55ec6fc83cf44876b8e7f25cdd393ff87b9eda6eb746085b61a6900de191f0ce2cb388d61ece52e78bc47368194e8e00277e0d1631e6b9d4626ef76f8522582ccd5a40be3febc699bb510acc6271d55ff0f4cf3bb7669855a72efd9ca3e1056a2fe592a5bc877cce2b1f63b58383971da87873d2d1349cf5881242cdce4e7e2c5c514755746a0e0a7c2a6d9701cde005ae3420beb17c379a3516662253554f51f0423bb1844b0b90c54ed8177ceb0e1036a6609d836e748ca06c40ca64befadc6443ec286a0ce464678e8d11eb455f7bb305acebf6cb1f50e394a9bfeb752df1687831bac9cdd811f4f112ef6658d0f8799a866374ff96c5e2b79f30e7a74f8a2bc9ed1f88f01f30e30cb78ffb2bff10108f35e910ee3be4463e9e6f0ed910e8d598326e71dfa2277ffe5579d7fe9b6018bfe295b25219eae07b3b0270665c3fa00c3e0d180812b5cd62925585de84a7c48a9a86dba96544a251654d1966e082432dc85b6149cf21e91a46020ec32b66d28ba3b6a90c0617bc6fdd55aea819af2bcf84864ad60c28fe3c9f8339d0aee68b39d97f63b6e082835d86119cf9b9fdc8b827c847ce40aa10e1577a710132316845e825345e95bdf94d0c66ec65a6c4319fce4792313663b5f7a651a6710783e6ab71608ac6cbbf3af6911adf596ccf7c172b9bd5bceb6db379967b32b143bdd11d2ee12ddf64ecef6391e0f8570e6cddd3db95204919362b89b739fa94e7c1bfde799fd5e22aa25ca6ca42e30c08e23aae2385d99ebab441072a880dcefdab74a4c9bd39d363f6d1933d59400fca161d432aa00f23b1b1c19a154be8989699d549b66d44e39896f5523443bc6ddf4a65e91f1f3fb7b52318869a05856a4fc92f3694c81ed833c972fb918f7e5\",\"cipherparams\":{\"iv\":\"8c46d6162cd4c765759aedcbce2a5874\"},\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"n\":262144,\"p\":1,\"r\":8,\"salt\":\"82fb6cdc6917609135277badacf15baa31899d08b71a5a0fa33167167c161537\"},\"mac\":\"9187b17f7eca48e6b8c586b0cd790dbe0feb876ac8385f93faa7d5e22a3c8fc7\"},\"id\":\"92caf6ee-2d43-48c0-859e-ffa1e0e23312\",\"version\":3}",
|
|
1417
|
-
)};
|
|
1418
|
-
const TEST_WALLET_PASSPHRASE = "QuantumCoinExample123!";
|
|
1419
|
-
|
|
1420
|
-
describe("all contracts", () => {
|
|
1421
|
-
it("deploys and invokes all contracts", async (t) => {
|
|
1422
|
-
const rpcUrl = process.env.QC_RPC_URL;
|
|
1423
|
-
if (!rpcUrl) {
|
|
1424
|
-
t.skip("QC_RPC_URL not provided");
|
|
1425
|
-
return;
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
const chainId = process.env.QC_CHAIN_ID ? Number(process.env.QC_CHAIN_ID) : 123123;
|
|
1429
|
-
await Initialize(null);
|
|
1430
|
-
|
|
1431
|
-
const provider = getProvider(rpcUrl, chainId);
|
|
1432
|
-
const wallet = Wallet.fromEncryptedJsonSync(TEST_WALLET_ENCRYPTED_JSON, TEST_WALLET_PASSPHRASE, provider);
|
|
1433
|
-
|
|
1434
|
-
let deployGasLimit = 600000;
|
|
1435
|
-
try {
|
|
1436
|
-
const firstFactory = new ${artifacts[0].contractName}__factory(wallet);
|
|
1437
|
-
const sampleReq = firstFactory.getDeployTransaction(${firstMeta.ctorArgsExpr || ""});
|
|
1438
|
-
const est = await provider.estimateGas({ from: wallet.address, data: sampleReq.data });
|
|
1439
|
-
deployGasLimit = Number(est + 200_000n);
|
|
1440
|
-
} catch {
|
|
1441
|
-
deployGasLimit = 6_000_000;
|
|
1442
|
-
}
|
|
1443
|
-
${deployAndInvokeBlocks}
|
|
1444
|
-
}, { timeout: ${Math.max(900_000, 300_000 * artifacts.length)}});
|
|
1445
|
-
});
|
|
1446
|
-
`;
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
/**
|
|
1450
|
-
* @typedef {Object} GenerateOptions
|
|
1451
|
-
* @property {string} abiPath
|
|
1452
|
-
* @property {string} binPath
|
|
1453
|
-
* @property {string} outDir
|
|
1454
|
-
* @property {string} contractName
|
|
1455
|
-
* @property {string=} packageName
|
|
1456
|
-
* @property {boolean=} createPackage
|
|
1457
|
-
* @property {Record<string,string>=} dependencies
|
|
1458
|
-
*/
|
|
1459
|
-
|
|
1460
|
-
/**
|
|
1461
|
-
* Generate multiple typed contract files.
|
|
1462
|
-
* @param {{ outDir: string, artifacts: Array<{ contractName: string, abi: any[], bytecode: string }> }} opts
|
|
1463
|
-
* @returns {{ contracts: Array<{ contractFile: string, factoryFile: string }>, typesFile: string, indexFile: string }}
|
|
1464
|
-
*/
|
|
1465
|
-
function generateFromArtifacts(opts) {
|
|
1466
|
-
_ensureDir(opts.outDir);
|
|
1467
|
-
const lang = (opts && opts.lang) || "ts"; // "ts" | "js"
|
|
1468
|
-
if (lang !== "ts" && lang !== "js") {
|
|
1469
|
-
throw new Error(`Unsupported generator lang: ${lang}`);
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
// Reject attacker-controlled names before they are interpolated into
|
|
1473
|
-
// generated source or used to build output file paths.
|
|
1474
|
-
for (const a of opts.artifacts || []) {
|
|
1475
|
-
_assertSafeIdentifier(a && a.contractName, "contract name");
|
|
1476
|
-
_assertSafeAbiNames(a && a.abi);
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
const contractNames = opts.artifacts.map((a) => a.contractName);
|
|
1480
|
-
|
|
1481
|
-
const typesFile = path.join(opts.outDir, lang === "ts" ? `types.ts` : `types.js`);
|
|
1482
|
-
const indexFile = path.join(opts.outDir, lang === "ts" ? `index.ts` : `index.js`);
|
|
1483
|
-
|
|
1484
|
-
fs.writeFileSync(typesFile, lang === "ts" ? _typesTs() : _typesJs(), "utf8");
|
|
1485
|
-
|
|
1486
|
-
// For JS generation, we also emit .d.ts files (TS declarations) so JS packages remain typed.
|
|
1487
|
-
const typesDtsFile = lang === "js" ? path.join(opts.outDir, `types.d.ts`) : null;
|
|
1488
|
-
const indexDtsFile = lang === "js" ? path.join(opts.outDir, `index.d.ts`) : null;
|
|
1489
|
-
if (lang === "js") {
|
|
1490
|
-
fs.writeFileSync(typesDtsFile, _typesDts(), "utf8");
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
const contracts = [];
|
|
1494
|
-
for (const a of opts.artifacts) {
|
|
1495
|
-
const contractFile = path.join(opts.outDir, `${a.contractName}.${lang === "ts" ? "ts" : "js"}`);
|
|
1496
|
-
const factoryFile = path.join(opts.outDir, `${a.contractName}__factory.${lang === "ts" ? "ts" : "js"}`);
|
|
1497
|
-
const contractDtsFile = lang === "js" ? path.join(opts.outDir, `${a.contractName}.d.ts`) : null;
|
|
1498
|
-
const factoryDtsFile = lang === "js" ? path.join(opts.outDir, `${a.contractName}__factory.d.ts`) : null;
|
|
1499
|
-
|
|
1500
|
-
// Defense-in-depth — even though contractName is a validated identifier,
|
|
1501
|
-
// ensure no output path escapes the target directory.
|
|
1502
|
-
for (const f of [contractFile, factoryFile, contractDtsFile, factoryDtsFile]) {
|
|
1503
|
-
if (f) _assertWithinDir(opts.outDir, f);
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
if (lang === "ts") {
|
|
1507
|
-
fs.writeFileSync(
|
|
1508
|
-
contractFile,
|
|
1509
|
-
_renderContractTs({ contractName: a.contractName, abi: a.abi, bytecode: a.bytecode, docs: a.docs || null }),
|
|
1510
|
-
"utf8",
|
|
1511
|
-
);
|
|
1512
|
-
fs.writeFileSync(factoryFile, _renderFactoryTs({ contractName: a.contractName, abi: a.abi }), "utf8");
|
|
1513
|
-
} else {
|
|
1514
|
-
fs.writeFileSync(
|
|
1515
|
-
contractFile,
|
|
1516
|
-
_renderContractJs({ contractName: a.contractName, abi: a.abi, bytecode: a.bytecode, docs: a.docs || null }),
|
|
1517
|
-
"utf8",
|
|
1518
|
-
);
|
|
1519
|
-
fs.writeFileSync(factoryFile, _renderFactoryJs({ contractName: a.contractName, abi: a.abi }), "utf8");
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
if (lang === "js") {
|
|
1523
|
-
fs.writeFileSync(contractDtsFile, _renderContractDts({ contractName: a.contractName, abi: a.abi }), "utf8");
|
|
1524
|
-
fs.writeFileSync(factoryDtsFile, _renderFactoryDts({ contractName: a.contractName, abi: a.abi }), "utf8");
|
|
1525
|
-
}
|
|
1526
|
-
contracts.push({ contractFile, factoryFile });
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
fs.writeFileSync(indexFile, lang === "ts" ? _renderIndexTs(contractNames) : _renderIndexJs(contractNames), "utf8");
|
|
1530
|
-
if (lang === "js") {
|
|
1531
|
-
fs.writeFileSync(indexDtsFile, _renderIndexDts(contractNames), "utf8");
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
if (lang === "js") {
|
|
1535
|
-
return { contracts, typesFile, indexFile, typesDtsFile, indexDtsFile };
|
|
1536
|
-
}
|
|
1537
|
-
return { contracts, typesFile, indexFile };
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
/**
|
|
1541
|
-
* Generate typed contract files.
|
|
1542
|
-
* @param {GenerateOptions} opts
|
|
1543
|
-
* @returns {{ contractFile: string, factoryFile: string, typesFile: string, indexFile: string }}
|
|
1544
|
-
*/
|
|
1545
|
-
function generate(opts) {
|
|
1546
|
-
const abi = _readJson(opts.abiPath);
|
|
1547
|
-
const bytecodeRaw = fs.readFileSync(opts.binPath, "utf8").trim();
|
|
1548
|
-
const bytecode = bytecodeRaw.startsWith("0x") ? bytecodeRaw : "0x" + bytecodeRaw;
|
|
1549
|
-
|
|
1550
|
-
_ensureDir(opts.outDir);
|
|
1551
|
-
const contractName = opts.contractName;
|
|
1552
|
-
|
|
1553
|
-
const res = generateFromArtifacts({
|
|
1554
|
-
outDir: opts.outDir,
|
|
1555
|
-
artifacts: [{ contractName, abi, bytecode }],
|
|
1556
|
-
lang: opts.lang || "ts",
|
|
1557
|
-
});
|
|
1558
|
-
|
|
1559
|
-
const contractFile = res.contracts[0].contractFile;
|
|
1560
|
-
const factoryFile = res.contracts[0].factoryFile;
|
|
1561
|
-
return { contractFile, factoryFile, typesFile: res.typesFile, indexFile: res.indexFile };
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
module.exports = {
|
|
1565
|
-
generate,
|
|
1566
|
-
generateFromArtifacts,
|
|
1567
|
-
generateTransactionalTestJs,
|
|
1568
|
-
generateAllContractsTransactionalTestJs,
|
|
1569
|
-
assertSafeIdentifier: _assertSafeIdentifier,
|
|
1570
|
-
};
|
|
1571
|
-
|