quantumcoin 7.0.3 → 7.0.5
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/.github/workflows/publish-npmjs.yaml +22 -22
- package/.gitignore +15 -15
- package/LICENSE +21 -21
- package/README-SDK.md +758 -754
- package/README.md +165 -150
- package/SPEC.md +3845 -3843
- package/config.d.ts +50 -50
- package/config.js +115 -115
- package/examples/AllSolidityTypes.sol +184 -184
- package/examples/SimpleIERC20.sol +74 -74
- package/examples/events.js +41 -35
- package/examples/events.ts +35 -0
- package/examples/example-generator-sdk-js.js +100 -95
- package/examples/example-generator-sdk-js.ts +77 -0
- package/examples/example-generator-sdk-ts.js +100 -95
- package/examples/example-generator-sdk-ts.ts +77 -0
- package/examples/example.js +72 -61
- package/examples/example.ts +61 -0
- package/examples/offline-signing.js +79 -73
- package/examples/offline-signing.ts +66 -0
- package/examples/package-lock.json +596 -57
- package/examples/package.json +32 -16
- package/examples/read-operations.js +32 -27
- package/examples/read-operations.ts +31 -0
- package/examples/sdk-generator-erc20.inline.json +251 -251
- package/examples/solidity-types.ts +43 -43
- package/examples/wallet-offline.js +35 -29
- package/examples/wallet-offline.ts +34 -0
- package/generate-sdk.js +1824 -1490
- package/index.js +12 -12
- package/package.json +95 -75
- package/scripts/copy-declarations.js +31 -0
- package/scripts/run-all-one-by-one.js +151 -0
- package/src/abi/fragments.d.ts +42 -42
- package/src/abi/fragments.js +63 -63
- package/src/abi/index.d.ts +13 -13
- package/src/abi/index.js +9 -9
- package/src/abi/interface.d.ts +128 -132
- package/src/abi/interface.js +590 -590
- package/src/abi/js-abi-coder.d.ts +8 -0
- package/src/abi/js-abi-coder.js +474 -474
- package/src/constants.d.ts +66 -61
- package/src/constants.js +101 -94
- package/src/contract/contract-factory.d.ts +28 -28
- package/src/contract/contract-factory.js +105 -105
- package/src/contract/contract.d.ts +113 -114
- package/src/contract/contract.js +354 -354
- package/src/contract/index.d.ts +9 -9
- package/src/contract/index.js +9 -9
- package/src/errors/index.d.ts +92 -92
- package/src/errors/index.js +188 -188
- package/src/generator/index.d.ts +74 -0
- package/src/generator/index.js +1404 -1404
- package/src/index.d.ts +125 -127
- package/src/index.js +41 -41
- package/src/internal/hex.d.ts +61 -61
- package/src/internal/hex.js +144 -144
- package/src/providers/extra-providers.d.ts +139 -128
- package/src/providers/extra-providers.js +600 -575
- package/src/providers/index.d.ts +17 -16
- package/src/providers/index.js +10 -10
- package/src/providers/json-rpc-provider.d.ts +12 -12
- package/src/providers/json-rpc-provider.js +79 -79
- package/src/providers/provider.d.ts +208 -203
- package/src/providers/provider.js +393 -371
- package/src/types/index.d.ts +214 -462
- package/src/types/index.js +9 -9
- package/src/utils/address.d.ts +72 -72
- package/src/utils/address.js +181 -182
- package/src/utils/encoding.d.ts +120 -120
- package/src/utils/encoding.js +306 -306
- package/src/utils/hashing.d.ts +82 -76
- package/src/utils/hashing.js +313 -298
- package/src/utils/index.d.ts +65 -55
- package/src/utils/index.js +13 -13
- package/src/utils/result.d.ts +57 -57
- package/src/utils/result.js +128 -128
- package/src/utils/rlp.d.ts +12 -12
- package/src/utils/rlp.js +200 -200
- package/src/utils/units.d.ts +29 -29
- package/src/utils/units.js +107 -107
- package/src/wallet/index.d.ts +10 -10
- package/src/wallet/index.js +8 -8
- package/src/wallet/wallet.d.ts +168 -160
- package/src/wallet/wallet.js +500 -489
- package/test/e2e/all-solidity-types.dynamic.test.js +207 -200
- package/test/e2e/all-solidity-types.dynamic.test.ts +191 -0
- package/test/e2e/all-solidity-types.fixtures.js +231 -231
- package/test/e2e/all-solidity-types.generated-sdks.e2e.test.js +387 -368
- package/test/e2e/all-solidity-types.generated-sdks.e2e.test.ts +350 -0
- package/test/e2e/helpers.js +59 -47
- package/test/e2e/signing-context-and-fee.e2e.test.js +141 -0
- package/test/e2e/signing-context-and-fee.e2e.test.ts +128 -0
- package/test/e2e/simple-erc20.generated-sdks.e2e.test.js +168 -151
- package/test/e2e/simple-erc20.generated-sdks.e2e.test.ts +141 -0
- package/test/e2e/transactional.test.js +245 -191
- package/test/e2e/transactional.test.ts +208 -0
- package/test/e2e/typed-generator.e2e.test.js +407 -404
- package/test/e2e/typed-generator.e2e.test.ts +337 -0
- package/test/fixtures/ConstructorParam.sol +23 -23
- package/test/fixtures/MultiContracts.sol +37 -37
- package/test/fixtures/SimpleStorage.sol +18 -18
- package/test/fixtures/StakingContract.abi.json +1 -1
- package/test/integration/ipc-provider.test.js +49 -44
- package/test/integration/ipc-provider.test.ts +44 -0
- package/test/integration/provider.test.js +88 -72
- package/test/integration/provider.test.ts +85 -0
- package/test/integration/ws-provider.test.js +41 -33
- package/test/integration/ws-provider.test.ts +38 -0
- package/test/security/malformed-input.test.js +37 -31
- package/test/security/malformed-input.test.ts +35 -0
- package/test/unit/_encrypted-output.txt +6 -0
- package/test/unit/_log-encrypted-jsons.js +45 -0
- package/test/unit/_write-keystore-fixture.js +16 -0
- package/test/unit/abi-interface.test.js +103 -98
- package/test/unit/abi-interface.test.ts +102 -0
- package/test/unit/address-wallet.test.js +392 -257
- package/test/unit/address-wallet.test.ts +379 -0
- package/test/unit/browser-provider.test.js +85 -82
- package/test/unit/browser-provider.test.ts +79 -0
- package/test/unit/contract.test.js +85 -82
- package/test/unit/contract.test.ts +83 -0
- package/test/unit/encoding-units-rlp.test.js +92 -89
- package/test/unit/encoding-units-rlp.test.ts +91 -0
- package/test/unit/errors.test.js +77 -74
- package/test/unit/errors.test.ts +76 -0
- package/test/unit/filter-by-blockhash.test.js +55 -52
- package/test/unit/filter-by-blockhash.test.ts +54 -0
- package/test/unit/fixtures/encrypted-keystores-48-32-36.js +9 -0
- package/test/unit/generate-contract-cli.test.js +42 -39
- package/test/unit/generate-contract-cli.test.ts +41 -0
- package/test/unit/generate-sdk-artifacts-json.test.js +113 -110
- package/test/unit/generate-sdk-artifacts-json.test.ts +110 -0
- package/test/unit/generator.test.js +102 -99
- package/test/unit/generator.test.ts +101 -0
- package/test/unit/hashing.test.js +68 -54
- package/test/unit/hashing.test.ts +67 -0
- package/test/unit/init.test.js +39 -36
- package/test/unit/init.test.ts +38 -0
- package/test/unit/interface.test.js +56 -53
- package/test/unit/interface.test.ts +54 -0
- package/test/unit/internal-hex.test.js +50 -47
- package/test/unit/internal-hex.test.ts +49 -0
- package/test/unit/populate-transaction.test.js +65 -62
- package/test/unit/populate-transaction.test.ts +64 -0
- package/test/unit/providers.test.js +200 -144
- package/test/unit/providers.test.ts +196 -0
- package/test/unit/result.test.js +80 -77
- package/test/unit/result.test.ts +79 -0
- package/test/unit/solidity-types.test.js +49 -46
- package/test/unit/solidity-types.test.ts +39 -0
- package/test/unit/utils.test.js +57 -54
- package/test/unit/utils.test.ts +56 -0
- package/test/verbose-logger.js +74 -0
- package/tsconfig.build.json +14 -0
package/src/abi/interface.js
CHANGED
|
@@ -1,590 +1,590 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Interface and ABI encoding/decoding helpers.
|
|
3
|
-
*
|
|
4
|
-
* This is a compatibility layer modelled after ethers.js v6 `Interface`,
|
|
5
|
-
* but adapted for QuantumCoin. ABI packing/unpacking is delegated to
|
|
6
|
-
* `quantum-coin-js-sdk` (WASM) as required by SPEC.md.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const qcsdk = require("quantum-coin-js-sdk");
|
|
10
|
-
const { makeError, assertArgument } = require("../errors");
|
|
11
|
-
const { normalizeHex, arrayify, bytesToHex } = require("../internal/hex");
|
|
12
|
-
const { EventFragment, FunctionFragment, ErrorFragment, ConstructorFragment } = require("./fragments");
|
|
13
|
-
const { Result } = require("../utils/result");
|
|
14
|
-
const { id } = require("../utils/hashing");
|
|
15
|
-
const jsAbi = require("./js-abi-coder");
|
|
16
|
-
|
|
17
|
-
function _requireInitialized() {
|
|
18
|
-
// eslint-disable-next-line global-require
|
|
19
|
-
const { isInitialized } = require("../../config");
|
|
20
|
-
if (!isInitialized()) {
|
|
21
|
-
throw makeError("QuantumCoin SDK not initialized. Call Initialize() first.", "UNKNOWN_ERROR", {
|
|
22
|
-
operation: "abi",
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function _sanitizeArg(value) {
|
|
28
|
-
// quantum-coin-js-sdk WASM interop does not accept BigInt values directly.
|
|
29
|
-
// Convert bigint to a decimal string (ethers-like behaviour).
|
|
30
|
-
if (typeof value === "bigint") return value.toString();
|
|
31
|
-
if (Array.isArray(value)) return value.map(_sanitizeArg);
|
|
32
|
-
if (value && typeof value === "object") {
|
|
33
|
-
// Avoid mutating user input; shallow clone.
|
|
34
|
-
const out = {};
|
|
35
|
-
for (const k of Object.keys(value)) out[k] = _sanitizeArg(value[k]);
|
|
36
|
-
return out;
|
|
37
|
-
}
|
|
38
|
-
return value;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function _sanitizeArgs(values) {
|
|
42
|
-
return (Array.isArray(values) ? values : []).map(_sanitizeArg);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function _asAbiArray(abi) {
|
|
46
|
-
if (abi == null) return [];
|
|
47
|
-
if (Array.isArray(abi)) return abi;
|
|
48
|
-
if (typeof abi === "object" && typeof abi.formatJson === "function") return JSON.parse(abi.formatJson());
|
|
49
|
-
throw makeError("invalid ABI", "INVALID_ARGUMENT", { abi });
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function _isNumericString(v) {
|
|
53
|
-
return typeof v === "string" && /^-?\d+$/.test(v.trim());
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function _normalizeParamTypeForQcsdk(type, components) {
|
|
57
|
-
if (typeof type !== "string") return type;
|
|
58
|
-
|
|
59
|
-
// Arrays preserve their shape; normalize the element type.
|
|
60
|
-
if (type.endsWith("]")) {
|
|
61
|
-
const inner = type.slice(0, type.lastIndexOf("["));
|
|
62
|
-
const suffix = type.slice(type.lastIndexOf("["));
|
|
63
|
-
return _normalizeParamTypeForQcsdk(inner, components) + suffix;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Tuples: normalize component types recursively.
|
|
67
|
-
if (type === "tuple") return "tuple";
|
|
68
|
-
|
|
69
|
-
// quantum-coin-js-sdk currently only reliably packs int256/uint256.
|
|
70
|
-
if (type === "uint" || type.startsWith("uint")) return "uint256";
|
|
71
|
-
if (type === "int" || type.startsWith("int")) return "int256";
|
|
72
|
-
|
|
73
|
-
// Fixed-size bytes (bytes1..bytes32) are encoded as a single 32-byte word.
|
|
74
|
-
// Encode them via uint256 (big-endian), with right-padding applied.
|
|
75
|
-
if (type.startsWith("bytes")) {
|
|
76
|
-
const m = type.match(/^bytes(\d+)$/);
|
|
77
|
-
if (m) return "uint256";
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return type;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function _normalizeAbiForQcsdk(abi) {
|
|
84
|
-
const out = Array.isArray(abi) ? abi.map((f) => ({ ...(f || {}) })) : [];
|
|
85
|
-
for (const f of out) {
|
|
86
|
-
if (!f || typeof f !== "object") continue;
|
|
87
|
-
const inputs = Array.isArray(f.inputs) ? f.inputs : [];
|
|
88
|
-
const outputs = Array.isArray(f.outputs) ? f.outputs : [];
|
|
89
|
-
const normalizeParams = (params) =>
|
|
90
|
-
params.map((p) => {
|
|
91
|
-
const np = { ...(p || {}) };
|
|
92
|
-
np.type = _normalizeParamTypeForQcsdk(String(np.type || ""), np.components);
|
|
93
|
-
if (Array.isArray(np.components) && np.components.length > 0) {
|
|
94
|
-
np.components = normalizeParams(np.components);
|
|
95
|
-
}
|
|
96
|
-
return np;
|
|
97
|
-
});
|
|
98
|
-
f.inputs = normalizeParams(inputs);
|
|
99
|
-
if (f.type === "function") f.outputs = normalizeParams(outputs);
|
|
100
|
-
if (f.type === "event") f.inputs = normalizeParams(inputs);
|
|
101
|
-
if (f.type === "error") f.inputs = normalizeParams(inputs);
|
|
102
|
-
if (f.type === "constructor") f.inputs = normalizeParams(inputs);
|
|
103
|
-
}
|
|
104
|
-
return out;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function _fixedBytesToUint256Decimal(type, value) {
|
|
108
|
-
// Allow already-normalized numeric values.
|
|
109
|
-
if (typeof value === "number") return value;
|
|
110
|
-
if (_isNumericString(value)) return value.trim();
|
|
111
|
-
|
|
112
|
-
const m = String(type || "").match(/^bytes(\d+)$/);
|
|
113
|
-
const n = m ? Number(m[1]) : 0;
|
|
114
|
-
if (!Number.isFinite(n) || n < 1 || n > 32) return value;
|
|
115
|
-
|
|
116
|
-
const b = arrayify(value);
|
|
117
|
-
if (b.length > n) throw makeError("fixed bytes value exceeds length", "INVALID_ARGUMENT", { type, length: b.length });
|
|
118
|
-
|
|
119
|
-
// Right-pad to N bytes, then right-pad to 32 bytes (Solidity fixed bytes encoding).
|
|
120
|
-
const fixed = new Uint8Array(n);
|
|
121
|
-
fixed.set(b, 0);
|
|
122
|
-
const word = new Uint8Array(32);
|
|
123
|
-
word.set(fixed, 0);
|
|
124
|
-
|
|
125
|
-
const hexWord = bytesToHex(word); // 0x + 64 hex
|
|
126
|
-
// Convert to decimal string for qcsdk (it can parse uint256 from decimal string).
|
|
127
|
-
return BigInt(hexWord).toString();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function _uint256ToFixedBytesHex(type, value) {
|
|
131
|
-
const m = String(type || "").match(/^bytes(\d+)$/);
|
|
132
|
-
const n = m ? Number(m[1]) : 0;
|
|
133
|
-
if (!Number.isFinite(n) || n < 1 || n > 32) return value;
|
|
134
|
-
if (value == null) return value;
|
|
135
|
-
|
|
136
|
-
const bi = typeof value === "bigint" ? value : BigInt(value);
|
|
137
|
-
let hex = bi.toString(16);
|
|
138
|
-
if (hex.length > 64) hex = hex.slice(-64);
|
|
139
|
-
hex = hex.padStart(64, "0");
|
|
140
|
-
// bytesN is the first N bytes (right-padded in the 32-byte word).
|
|
141
|
-
return normalizeHex("0x" + hex.slice(0, n * 2));
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function _convertInputValueForQcsdk(param, value) {
|
|
145
|
-
const type = String(param && param.type ? param.type : "");
|
|
146
|
-
|
|
147
|
-
// Apply bigint sanitization early.
|
|
148
|
-
const v = _sanitizeArg(value);
|
|
149
|
-
|
|
150
|
-
// Arrays
|
|
151
|
-
if (type.endsWith("]")) {
|
|
152
|
-
const innerType = type.slice(0, type.lastIndexOf("["));
|
|
153
|
-
const innerParam = { ...(param || {}), type: innerType };
|
|
154
|
-
if (!Array.isArray(v)) return v;
|
|
155
|
-
return v.map((x) => _convertInputValueForQcsdk(innerParam, x));
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Tuples
|
|
159
|
-
if (type === "tuple") {
|
|
160
|
-
const comps = Array.isArray(param && param.components) ? param.components : [];
|
|
161
|
-
if (Array.isArray(v)) {
|
|
162
|
-
return v.map((x, idx) => _convertInputValueForQcsdk(comps[idx] || { type: "uint256" }, x));
|
|
163
|
-
}
|
|
164
|
-
if (v && typeof v === "object") {
|
|
165
|
-
const out = {};
|
|
166
|
-
for (let i = 0; i < comps.length; i++) {
|
|
167
|
-
const c = comps[i];
|
|
168
|
-
const name = c && typeof c.name === "string" && c.name ? c.name : String(i);
|
|
169
|
-
out[name] = _convertInputValueForQcsdk(c, v[name] != null ? v[name] : v[String(i)]);
|
|
170
|
-
}
|
|
171
|
-
return out;
|
|
172
|
-
}
|
|
173
|
-
return v;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Fixed bytes
|
|
177
|
-
const mBytes = type.match(/^bytes(\d+)$/);
|
|
178
|
-
if (mBytes) {
|
|
179
|
-
return _fixedBytesToUint256Decimal(type, v);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return v;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function _convertOutputValueFromQcsdk(param, value) {
|
|
186
|
-
const type = String(param && param.type ? param.type : "");
|
|
187
|
-
|
|
188
|
-
if (type.endsWith("]")) {
|
|
189
|
-
const innerType = type.slice(0, type.lastIndexOf("["));
|
|
190
|
-
const innerParam = { ...(param || {}), type: innerType };
|
|
191
|
-
if (!Array.isArray(value)) return value;
|
|
192
|
-
return value.map((x) => _convertOutputValueFromQcsdk(innerParam, x));
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (type === "tuple") {
|
|
196
|
-
const comps = Array.isArray(param && param.components) ? param.components : [];
|
|
197
|
-
if (Array.isArray(value)) {
|
|
198
|
-
return value.map((x, idx) => _convertOutputValueFromQcsdk(comps[idx] || { type: "uint256" }, x));
|
|
199
|
-
}
|
|
200
|
-
if (value && typeof value === "object") {
|
|
201
|
-
const out = {};
|
|
202
|
-
for (let i = 0; i < comps.length; i++) {
|
|
203
|
-
const c = comps[i];
|
|
204
|
-
const name = c && typeof c.name === "string" && c.name ? c.name : String(i);
|
|
205
|
-
const raw = Object.prototype.hasOwnProperty.call(value, name)
|
|
206
|
-
? value[name]
|
|
207
|
-
: Object.prototype.hasOwnProperty.call(value, String(i))
|
|
208
|
-
? value[String(i)]
|
|
209
|
-
: undefined;
|
|
210
|
-
out[name] = _convertOutputValueFromQcsdk(c, raw);
|
|
211
|
-
}
|
|
212
|
-
return out;
|
|
213
|
-
}
|
|
214
|
-
return value;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const mBytes = type.match(/^bytes(\d+)$/);
|
|
218
|
-
if (mBytes) {
|
|
219
|
-
return _uint256ToFixedBytesHex(type, value);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Normalize integer outputs to bigint for consistency with core typings.
|
|
223
|
-
if (type === "uint" || type.startsWith("uint") || type === "int" || type.startsWith("int")) {
|
|
224
|
-
if (value == null) return value;
|
|
225
|
-
if (typeof value === "bigint") return value;
|
|
226
|
-
try {
|
|
227
|
-
return BigInt(value);
|
|
228
|
-
} catch {
|
|
229
|
-
return value;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return value;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
class Interface {
|
|
237
|
-
/**
|
|
238
|
-
* @param {any[]|Interface} abi
|
|
239
|
-
*/
|
|
240
|
-
constructor(abi) {
|
|
241
|
-
this.abi = _asAbiArray(abi);
|
|
242
|
-
this._abiJson = JSON.stringify(this.abi);
|
|
243
|
-
this._qcsdkAbi = _normalizeAbiForQcsdk(this.abi);
|
|
244
|
-
this._qcsdkAbiJson = JSON.stringify(this._qcsdkAbi);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Returns JSON format of ABI.
|
|
249
|
-
* @returns {string}
|
|
250
|
-
*/
|
|
251
|
-
formatJson() {
|
|
252
|
-
return this._abiJson;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Internal: normalized ABI JSON for qcsdk.
|
|
257
|
-
* @returns {string}
|
|
258
|
-
*/
|
|
259
|
-
_qcsdkFormatJson() {
|
|
260
|
-
return this._qcsdkAbiJson;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Internal: normalize argument values for qcsdk based on original ABI params.
|
|
265
|
-
* @param {Array<any>} params
|
|
266
|
-
* @param {Array<any>} values
|
|
267
|
-
* @returns {Array<any>}
|
|
268
|
-
*/
|
|
269
|
-
_qcsdkNormalizeValues(params, values) {
|
|
270
|
-
const ps = Array.isArray(params) ? params : [];
|
|
271
|
-
const vs = Array.isArray(values) ? values : [];
|
|
272
|
-
return ps.map((p, i) => _convertInputValueForQcsdk(p, vs[i]));
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Basic formatter (ethers supports multiple formats).
|
|
277
|
-
* @param {string=} format
|
|
278
|
-
* @returns {string}
|
|
279
|
-
*/
|
|
280
|
-
format(format) {
|
|
281
|
-
void format;
|
|
282
|
-
return this._abiJson;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Get a function fragment by name (first match).
|
|
287
|
-
* @param {string} nameOrSignature
|
|
288
|
-
* @returns {FunctionFragment}
|
|
289
|
-
*/
|
|
290
|
-
getFunction(nameOrSignature) {
|
|
291
|
-
assertArgument(typeof nameOrSignature === "string", "name must be a string", "nameOrSignature", nameOrSignature);
|
|
292
|
-
const found = this.abi.find((f) => f && f.type === "function" && f.name === nameOrSignature);
|
|
293
|
-
if (!found) throw makeError("function not found", "INVALID_ARGUMENT", { nameOrSignature });
|
|
294
|
-
return new FunctionFragment(found);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Get an event fragment by name (first match).
|
|
299
|
-
* @param {string} nameOrSignature
|
|
300
|
-
* @returns {EventFragment}
|
|
301
|
-
*/
|
|
302
|
-
getEvent(nameOrSignature) {
|
|
303
|
-
assertArgument(typeof nameOrSignature === "string", "name must be a string", "nameOrSignature", nameOrSignature);
|
|
304
|
-
const found = this.abi.find((f) => f && f.type === "event" && f.name === nameOrSignature);
|
|
305
|
-
if (!found) throw makeError("event not found", "INVALID_ARGUMENT", { nameOrSignature });
|
|
306
|
-
return new EventFragment(found);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Get an error fragment by name (first match).
|
|
311
|
-
* @param {string} nameOrSignature
|
|
312
|
-
* @returns {ErrorFragment}
|
|
313
|
-
*/
|
|
314
|
-
getError(nameOrSignature) {
|
|
315
|
-
assertArgument(typeof nameOrSignature === "string", "name must be a string", "nameOrSignature", nameOrSignature);
|
|
316
|
-
const found = this.abi.find((f) => f && f.type === "error" && f.name === nameOrSignature);
|
|
317
|
-
if (!found) throw makeError("error not found", "INVALID_ARGUMENT", { nameOrSignature });
|
|
318
|
-
return new ErrorFragment(found);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Returns the constructor fragment if present.
|
|
323
|
-
* @returns {ConstructorFragment|null}
|
|
324
|
-
*/
|
|
325
|
-
getConstructor() {
|
|
326
|
-
const found = this.abi.find((f) => f && f.type === "constructor");
|
|
327
|
-
return found ? new ConstructorFragment(found) : null;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Encode function data using quantum-coin-js-sdk.
|
|
332
|
-
* @param {FunctionFragment|string} functionFragment
|
|
333
|
-
* @param {any[]} values
|
|
334
|
-
* @returns {string}
|
|
335
|
-
*/
|
|
336
|
-
encodeFunctionData(functionFragment, values) {
|
|
337
|
-
_requireInitialized();
|
|
338
|
-
const name = typeof functionFragment === "string" ? functionFragment : functionFragment?.name;
|
|
339
|
-
assertArgument(typeof name === "string" && name.length > 0, "invalid function", "functionFragment", functionFragment);
|
|
340
|
-
const frag = this.getFunction(name);
|
|
341
|
-
const inputs = Array.isArray(frag.inputs) ? frag.inputs : [];
|
|
342
|
-
const rawArgs = Array.isArray(values) ? values : [];
|
|
343
|
-
|
|
344
|
-
// Fallback for complex ABI surfaces where qcsdk packing is unreliable.
|
|
345
|
-
if (jsAbi.needsJsAbi(inputs)) {
|
|
346
|
-
return jsAbi.encodeFunctionData(name, inputs, rawArgs);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const args = inputs.map((p, idx) => _convertInputValueForQcsdk(p, rawArgs[idx]));
|
|
350
|
-
const res = qcsdk.packMethodData(this._qcsdkAbiJson, name, ...args);
|
|
351
|
-
if (!res || typeof res.error !== "string") throw makeError("packMethodData failed", "UNKNOWN_ERROR", {});
|
|
352
|
-
if (res.error) throw makeError(res.error, "UNKNOWN_ERROR", { operation: "packMethodData", function: name });
|
|
353
|
-
return normalizeHex(res.result);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Decode function result using quantum-coin-js-sdk.
|
|
358
|
-
* @param {FunctionFragment|string} functionFragment
|
|
359
|
-
* @param {string} data
|
|
360
|
-
* @returns {any}
|
|
361
|
-
*/
|
|
362
|
-
decodeFunctionResult(functionFragment, data) {
|
|
363
|
-
_requireInitialized();
|
|
364
|
-
const name = typeof functionFragment === "string" ? functionFragment : functionFragment?.name;
|
|
365
|
-
assertArgument(typeof name === "string" && name.length > 0, "invalid function", "functionFragment", functionFragment);
|
|
366
|
-
assertArgument(typeof data === "string", "data must be a hex string", "data", data);
|
|
367
|
-
const frag = this.getFunction(name);
|
|
368
|
-
const outputs = Array.isArray(frag.outputs) ? frag.outputs : [];
|
|
369
|
-
|
|
370
|
-
// Fallback for complex output surfaces.
|
|
371
|
-
if (jsAbi.needsJsAbi(outputs)) {
|
|
372
|
-
return jsAbi.decodeFunctionResult(outputs, data);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
const res = qcsdk.unpackMethodData(this._qcsdkAbiJson, name, data);
|
|
376
|
-
if (!res || typeof res.error !== "string") throw makeError("unpackMethodData failed", "UNKNOWN_ERROR", {});
|
|
377
|
-
if (res.error) throw makeError(res.error, "UNKNOWN_ERROR", { operation: "unpackMethodData", function: name });
|
|
378
|
-
try {
|
|
379
|
-
const decoded = JSON.parse(res.result);
|
|
380
|
-
// qcsdk generally returns an array for function outputs; convert bytesN back to hex.
|
|
381
|
-
if (Array.isArray(decoded)) {
|
|
382
|
-
return decoded.map((v, i) => _convertOutputValueFromQcsdk(outputs[i] || { type: "uint256" }, v));
|
|
383
|
-
}
|
|
384
|
-
return _convertOutputValueFromQcsdk(outputs[0] || { type: "uint256" }, decoded);
|
|
385
|
-
} catch {
|
|
386
|
-
return res.result;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Encode an event log from values.
|
|
392
|
-
* @param {EventFragment|any} eventFragment
|
|
393
|
-
* @param {any[]} values
|
|
394
|
-
* @returns {{ topics: string[], data: string }}
|
|
395
|
-
*/
|
|
396
|
-
encodeEventLog(eventFragment, values) {
|
|
397
|
-
_requireInitialized();
|
|
398
|
-
const name = typeof eventFragment === "string" ? eventFragment : eventFragment?.name;
|
|
399
|
-
assertArgument(typeof name === "string" && name.length > 0, "invalid event", "eventFragment", eventFragment);
|
|
400
|
-
const frag = this.getEvent(name);
|
|
401
|
-
const inputs = Array.isArray(frag.inputs) ? frag.inputs : [];
|
|
402
|
-
const rawArgs = Array.isArray(values) ? values : [];
|
|
403
|
-
const args = inputs.map((p, idx) => _convertInputValueForQcsdk(p, rawArgs[idx]));
|
|
404
|
-
const res = qcsdk.encodeEventLog(this._qcsdkAbiJson, name, ...args);
|
|
405
|
-
if (!res || typeof res.error !== "string") throw makeError("encodeEventLog failed", "UNKNOWN_ERROR", {});
|
|
406
|
-
if (res.error) throw makeError(res.error, "UNKNOWN_ERROR", { operation: "encodeEventLog", event: name });
|
|
407
|
-
return res.result;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Decode an event log.
|
|
412
|
-
* @param {EventFragment|any} eventFragment
|
|
413
|
-
* @param {string[]} topics
|
|
414
|
-
* @param {string} data
|
|
415
|
-
* @returns {any}
|
|
416
|
-
*/
|
|
417
|
-
decodeEventLog(eventFragment, topics, data) {
|
|
418
|
-
_requireInitialized();
|
|
419
|
-
const name = typeof eventFragment === "string" ? eventFragment : eventFragment?.name;
|
|
420
|
-
assertArgument(typeof name === "string" && name.length > 0, "invalid event", "eventFragment", eventFragment);
|
|
421
|
-
assertArgument(Array.isArray(topics), "topics must be an array", "topics", topics);
|
|
422
|
-
assertArgument(typeof data === "string", "data must be a string", "data", data);
|
|
423
|
-
const frag = this.getEvent(name);
|
|
424
|
-
const inputs = Array.isArray(frag.inputs) ? frag.inputs : [];
|
|
425
|
-
const res = qcsdk.decodeEventLog(this._qcsdkAbiJson, name, topics, data);
|
|
426
|
-
if (!res || typeof res.error !== "string") throw makeError("decodeEventLog failed", "UNKNOWN_ERROR", {});
|
|
427
|
-
if (res.error) throw makeError(res.error, "UNKNOWN_ERROR", { operation: "decodeEventLog", event: name });
|
|
428
|
-
try {
|
|
429
|
-
const decoded = JSON.parse(res.result);
|
|
430
|
-
if (Array.isArray(decoded)) {
|
|
431
|
-
return decoded.map((v, i) => _convertOutputValueFromQcsdk(inputs[i] || { type: "uint256" }, v));
|
|
432
|
-
}
|
|
433
|
-
return decoded;
|
|
434
|
-
} catch {
|
|
435
|
-
return res.result;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// The following methods exist in ethers.js v6. We provide placeholders to keep API shape.
|
|
440
|
-
parseTransaction() {
|
|
441
|
-
throw makeError("parseTransaction not implemented", "NOT_IMPLEMENTED", {});
|
|
442
|
-
}
|
|
443
|
-
parseLog() {
|
|
444
|
-
_requireInitialized();
|
|
445
|
-
const log = arguments.length > 0 ? arguments[0] : null;
|
|
446
|
-
assertArgument(log && typeof log === "object", "log must be an object", "log", log);
|
|
447
|
-
|
|
448
|
-
const topics = log.topics;
|
|
449
|
-
const data = log.data;
|
|
450
|
-
assertArgument(Array.isArray(topics), "log.topics must be an array", "log.topics", topics);
|
|
451
|
-
assertArgument(typeof data === "string", "log.data must be a string", "log.data", data);
|
|
452
|
-
assertArgument(topics.length > 0, "log.topics must contain at least 1 topic", "log.topics", topics);
|
|
453
|
-
|
|
454
|
-
const topic0 = normalizeHex(String(topics[0]));
|
|
455
|
-
|
|
456
|
-
// Find event by signature topic. (Anonymous events cannot be auto-detected.)
|
|
457
|
-
/** @type {any|null} */
|
|
458
|
-
let matched = null;
|
|
459
|
-
/** @type {string|null} */
|
|
460
|
-
let signature = null;
|
|
461
|
-
|
|
462
|
-
for (const f of this.abi) {
|
|
463
|
-
if (!f || f.type !== "event") continue;
|
|
464
|
-
if (f.anonymous) continue;
|
|
465
|
-
if (!f.name) continue;
|
|
466
|
-
const inputs = Array.isArray(f.inputs) ? f.inputs : [];
|
|
467
|
-
const sig = `${f.name}(${inputs.map((i) => String(i.type || "")).join(",")})`;
|
|
468
|
-
const t = normalizeHex(id(sig));
|
|
469
|
-
if (t === topic0) {
|
|
470
|
-
matched = f;
|
|
471
|
-
signature = sig;
|
|
472
|
-
break;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if (!matched) {
|
|
477
|
-
throw makeError("no matching event for log.topics[0]", "INVALID_ARGUMENT", { topic0 });
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
const fragment = new EventFragment(matched);
|
|
481
|
-
const decoded = this.decodeEventLog(fragment.name, topics.map((t) => normalizeHex(String(t))), normalizeHex(data));
|
|
482
|
-
|
|
483
|
-
const inputs = Array.isArray(matched.inputs) ? matched.inputs : [];
|
|
484
|
-
const keys = inputs.map((i) => (i && typeof i.name === "string" && i.name.length ? i.name : null));
|
|
485
|
-
|
|
486
|
-
let items = [];
|
|
487
|
-
if (Array.isArray(decoded)) {
|
|
488
|
-
items = decoded;
|
|
489
|
-
} else if (decoded && typeof decoded === "object") {
|
|
490
|
-
items = inputs.map((i, idx) => {
|
|
491
|
-
const n = i && typeof i.name === "string" ? i.name : null;
|
|
492
|
-
if (n && Object.prototype.hasOwnProperty.call(decoded, n)) return decoded[n];
|
|
493
|
-
if (Object.prototype.hasOwnProperty.call(decoded, String(idx))) return decoded[String(idx)];
|
|
494
|
-
return undefined;
|
|
495
|
-
});
|
|
496
|
-
} else {
|
|
497
|
-
items = inputs.map(() => decoded);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
const args = Result.fromItems(items, keys);
|
|
501
|
-
return {
|
|
502
|
-
fragment,
|
|
503
|
-
name: fragment.name,
|
|
504
|
-
signature: signature || fragment.name,
|
|
505
|
-
topic: topic0,
|
|
506
|
-
args,
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
parseError() {
|
|
510
|
-
throw makeError("parseError not implemented", "NOT_IMPLEMENTED", {});
|
|
511
|
-
}
|
|
512
|
-
getSighash() {
|
|
513
|
-
throw makeError("getSighash not implemented", "NOT_IMPLEMENTED", {});
|
|
514
|
-
}
|
|
515
|
-
getEventTopic() {
|
|
516
|
-
throw makeError("getEventTopic not implemented", "NOT_IMPLEMENTED", {});
|
|
517
|
-
}
|
|
518
|
-
getFallback() {
|
|
519
|
-
return null;
|
|
520
|
-
}
|
|
521
|
-
getReceive() {
|
|
522
|
-
return null;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
class AbiCoder {
|
|
527
|
-
/**
|
|
528
|
-
* Encode values by types into ABI data.
|
|
529
|
-
* @param {(string|any)[]} types
|
|
530
|
-
* @param {any[]} values
|
|
531
|
-
* @returns {string}
|
|
532
|
-
*/
|
|
533
|
-
encode(types, values) {
|
|
534
|
-
_requireInitialized();
|
|
535
|
-
assertArgument(Array.isArray(types), "types must be an array", "types", types);
|
|
536
|
-
assertArgument(Array.isArray(values), "values must be an array", "values", values);
|
|
537
|
-
|
|
538
|
-
const abi = [
|
|
539
|
-
{
|
|
540
|
-
type: "function",
|
|
541
|
-
name: "__encode",
|
|
542
|
-
stateMutability: "pure",
|
|
543
|
-
inputs: types.map((t, i) => ({ name: `arg${i}`, type: String(t) })),
|
|
544
|
-
outputs: [],
|
|
545
|
-
},
|
|
546
|
-
];
|
|
547
|
-
const iface = new Interface(abi);
|
|
548
|
-
const full = iface.encodeFunctionData("__encode", values);
|
|
549
|
-
// Strip the 4-byte selector (8 hex chars) + 0x
|
|
550
|
-
return "0x" + full.slice(10);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* Decode ABI data by output types.
|
|
555
|
-
* @param {(string|any)[]} types
|
|
556
|
-
* @param {string} data
|
|
557
|
-
* @returns {any}
|
|
558
|
-
*/
|
|
559
|
-
decode(types, data) {
|
|
560
|
-
_requireInitialized();
|
|
561
|
-
assertArgument(Array.isArray(types), "types must be an array", "types", types);
|
|
562
|
-
assertArgument(typeof data === "string", "data must be a string", "data", data);
|
|
563
|
-
|
|
564
|
-
const abi = [
|
|
565
|
-
{
|
|
566
|
-
type: "function",
|
|
567
|
-
name: "__decode",
|
|
568
|
-
stateMutability: "pure",
|
|
569
|
-
inputs: [],
|
|
570
|
-
outputs: types.map((t, i) => ({ name: `ret${i}`, type: String(t) })),
|
|
571
|
-
},
|
|
572
|
-
];
|
|
573
|
-
const iface = new Interface(abi);
|
|
574
|
-
return iface.decodeFunctionResult("__decode", data);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* Return a default value for types.
|
|
579
|
-
* @param {(string|any)[]} types
|
|
580
|
-
* @returns {any}
|
|
581
|
-
*/
|
|
582
|
-
getDefaultValue(types) {
|
|
583
|
-
// Lightweight default; ethers returns a Result. Here we return array of nulls.
|
|
584
|
-
assertArgument(Array.isArray(types), "types must be an array", "types", types);
|
|
585
|
-
return types.map(() => null);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
module.exports = { Interface, AbiCoder };
|
|
590
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Interface and ABI encoding/decoding helpers.
|
|
3
|
+
*
|
|
4
|
+
* This is a compatibility layer modelled after ethers.js v6 `Interface`,
|
|
5
|
+
* but adapted for QuantumCoin. ABI packing/unpacking is delegated to
|
|
6
|
+
* `quantum-coin-js-sdk` (WASM) as required by SPEC.md.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const qcsdk = require("quantum-coin-js-sdk");
|
|
10
|
+
const { makeError, assertArgument } = require("../errors");
|
|
11
|
+
const { normalizeHex, arrayify, bytesToHex } = require("../internal/hex");
|
|
12
|
+
const { EventFragment, FunctionFragment, ErrorFragment, ConstructorFragment } = require("./fragments");
|
|
13
|
+
const { Result } = require("../utils/result");
|
|
14
|
+
const { id } = require("../utils/hashing");
|
|
15
|
+
const jsAbi = require("./js-abi-coder");
|
|
16
|
+
|
|
17
|
+
function _requireInitialized() {
|
|
18
|
+
// eslint-disable-next-line global-require
|
|
19
|
+
const { isInitialized } = require("../../config");
|
|
20
|
+
if (!isInitialized()) {
|
|
21
|
+
throw makeError("QuantumCoin SDK not initialized. Call Initialize() first.", "UNKNOWN_ERROR", {
|
|
22
|
+
operation: "abi",
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function _sanitizeArg(value) {
|
|
28
|
+
// quantum-coin-js-sdk WASM interop does not accept BigInt values directly.
|
|
29
|
+
// Convert bigint to a decimal string (ethers-like behaviour).
|
|
30
|
+
if (typeof value === "bigint") return value.toString();
|
|
31
|
+
if (Array.isArray(value)) return value.map(_sanitizeArg);
|
|
32
|
+
if (value && typeof value === "object") {
|
|
33
|
+
// Avoid mutating user input; shallow clone.
|
|
34
|
+
const out = {};
|
|
35
|
+
for (const k of Object.keys(value)) out[k] = _sanitizeArg(value[k]);
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function _sanitizeArgs(values) {
|
|
42
|
+
return (Array.isArray(values) ? values : []).map(_sanitizeArg);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function _asAbiArray(abi) {
|
|
46
|
+
if (abi == null) return [];
|
|
47
|
+
if (Array.isArray(abi)) return abi;
|
|
48
|
+
if (typeof abi === "object" && typeof abi.formatJson === "function") return JSON.parse(abi.formatJson());
|
|
49
|
+
throw makeError("invalid ABI", "INVALID_ARGUMENT", { abi });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function _isNumericString(v) {
|
|
53
|
+
return typeof v === "string" && /^-?\d+$/.test(v.trim());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function _normalizeParamTypeForQcsdk(type, components) {
|
|
57
|
+
if (typeof type !== "string") return type;
|
|
58
|
+
|
|
59
|
+
// Arrays preserve their shape; normalize the element type.
|
|
60
|
+
if (type.endsWith("]")) {
|
|
61
|
+
const inner = type.slice(0, type.lastIndexOf("["));
|
|
62
|
+
const suffix = type.slice(type.lastIndexOf("["));
|
|
63
|
+
return _normalizeParamTypeForQcsdk(inner, components) + suffix;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Tuples: normalize component types recursively.
|
|
67
|
+
if (type === "tuple") return "tuple";
|
|
68
|
+
|
|
69
|
+
// quantum-coin-js-sdk currently only reliably packs int256/uint256.
|
|
70
|
+
if (type === "uint" || type.startsWith("uint")) return "uint256";
|
|
71
|
+
if (type === "int" || type.startsWith("int")) return "int256";
|
|
72
|
+
|
|
73
|
+
// Fixed-size bytes (bytes1..bytes32) are encoded as a single 32-byte word.
|
|
74
|
+
// Encode them via uint256 (big-endian), with right-padding applied.
|
|
75
|
+
if (type.startsWith("bytes")) {
|
|
76
|
+
const m = type.match(/^bytes(\d+)$/);
|
|
77
|
+
if (m) return "uint256";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return type;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function _normalizeAbiForQcsdk(abi) {
|
|
84
|
+
const out = Array.isArray(abi) ? abi.map((f) => ({ ...(f || {}) })) : [];
|
|
85
|
+
for (const f of out) {
|
|
86
|
+
if (!f || typeof f !== "object") continue;
|
|
87
|
+
const inputs = Array.isArray(f.inputs) ? f.inputs : [];
|
|
88
|
+
const outputs = Array.isArray(f.outputs) ? f.outputs : [];
|
|
89
|
+
const normalizeParams = (params) =>
|
|
90
|
+
params.map((p) => {
|
|
91
|
+
const np = { ...(p || {}) };
|
|
92
|
+
np.type = _normalizeParamTypeForQcsdk(String(np.type || ""), np.components);
|
|
93
|
+
if (Array.isArray(np.components) && np.components.length > 0) {
|
|
94
|
+
np.components = normalizeParams(np.components);
|
|
95
|
+
}
|
|
96
|
+
return np;
|
|
97
|
+
});
|
|
98
|
+
f.inputs = normalizeParams(inputs);
|
|
99
|
+
if (f.type === "function") f.outputs = normalizeParams(outputs);
|
|
100
|
+
if (f.type === "event") f.inputs = normalizeParams(inputs);
|
|
101
|
+
if (f.type === "error") f.inputs = normalizeParams(inputs);
|
|
102
|
+
if (f.type === "constructor") f.inputs = normalizeParams(inputs);
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function _fixedBytesToUint256Decimal(type, value) {
|
|
108
|
+
// Allow already-normalized numeric values.
|
|
109
|
+
if (typeof value === "number") return value;
|
|
110
|
+
if (_isNumericString(value)) return value.trim();
|
|
111
|
+
|
|
112
|
+
const m = String(type || "").match(/^bytes(\d+)$/);
|
|
113
|
+
const n = m ? Number(m[1]) : 0;
|
|
114
|
+
if (!Number.isFinite(n) || n < 1 || n > 32) return value;
|
|
115
|
+
|
|
116
|
+
const b = arrayify(value);
|
|
117
|
+
if (b.length > n) throw makeError("fixed bytes value exceeds length", "INVALID_ARGUMENT", { type, length: b.length });
|
|
118
|
+
|
|
119
|
+
// Right-pad to N bytes, then right-pad to 32 bytes (Solidity fixed bytes encoding).
|
|
120
|
+
const fixed = new Uint8Array(n);
|
|
121
|
+
fixed.set(b, 0);
|
|
122
|
+
const word = new Uint8Array(32);
|
|
123
|
+
word.set(fixed, 0);
|
|
124
|
+
|
|
125
|
+
const hexWord = bytesToHex(word); // 0x + 64 hex
|
|
126
|
+
// Convert to decimal string for qcsdk (it can parse uint256 from decimal string).
|
|
127
|
+
return BigInt(hexWord).toString();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function _uint256ToFixedBytesHex(type, value) {
|
|
131
|
+
const m = String(type || "").match(/^bytes(\d+)$/);
|
|
132
|
+
const n = m ? Number(m[1]) : 0;
|
|
133
|
+
if (!Number.isFinite(n) || n < 1 || n > 32) return value;
|
|
134
|
+
if (value == null) return value;
|
|
135
|
+
|
|
136
|
+
const bi = typeof value === "bigint" ? value : BigInt(value);
|
|
137
|
+
let hex = bi.toString(16);
|
|
138
|
+
if (hex.length > 64) hex = hex.slice(-64);
|
|
139
|
+
hex = hex.padStart(64, "0");
|
|
140
|
+
// bytesN is the first N bytes (right-padded in the 32-byte word).
|
|
141
|
+
return normalizeHex("0x" + hex.slice(0, n * 2));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function _convertInputValueForQcsdk(param, value) {
|
|
145
|
+
const type = String(param && param.type ? param.type : "");
|
|
146
|
+
|
|
147
|
+
// Apply bigint sanitization early.
|
|
148
|
+
const v = _sanitizeArg(value);
|
|
149
|
+
|
|
150
|
+
// Arrays
|
|
151
|
+
if (type.endsWith("]")) {
|
|
152
|
+
const innerType = type.slice(0, type.lastIndexOf("["));
|
|
153
|
+
const innerParam = { ...(param || {}), type: innerType };
|
|
154
|
+
if (!Array.isArray(v)) return v;
|
|
155
|
+
return v.map((x) => _convertInputValueForQcsdk(innerParam, x));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Tuples
|
|
159
|
+
if (type === "tuple") {
|
|
160
|
+
const comps = Array.isArray(param && param.components) ? param.components : [];
|
|
161
|
+
if (Array.isArray(v)) {
|
|
162
|
+
return v.map((x, idx) => _convertInputValueForQcsdk(comps[idx] || { type: "uint256" }, x));
|
|
163
|
+
}
|
|
164
|
+
if (v && typeof v === "object") {
|
|
165
|
+
const out = {};
|
|
166
|
+
for (let i = 0; i < comps.length; i++) {
|
|
167
|
+
const c = comps[i];
|
|
168
|
+
const name = c && typeof c.name === "string" && c.name ? c.name : String(i);
|
|
169
|
+
out[name] = _convertInputValueForQcsdk(c, v[name] != null ? v[name] : v[String(i)]);
|
|
170
|
+
}
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
return v;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Fixed bytes
|
|
177
|
+
const mBytes = type.match(/^bytes(\d+)$/);
|
|
178
|
+
if (mBytes) {
|
|
179
|
+
return _fixedBytesToUint256Decimal(type, v);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return v;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function _convertOutputValueFromQcsdk(param, value) {
|
|
186
|
+
const type = String(param && param.type ? param.type : "");
|
|
187
|
+
|
|
188
|
+
if (type.endsWith("]")) {
|
|
189
|
+
const innerType = type.slice(0, type.lastIndexOf("["));
|
|
190
|
+
const innerParam = { ...(param || {}), type: innerType };
|
|
191
|
+
if (!Array.isArray(value)) return value;
|
|
192
|
+
return value.map((x) => _convertOutputValueFromQcsdk(innerParam, x));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (type === "tuple") {
|
|
196
|
+
const comps = Array.isArray(param && param.components) ? param.components : [];
|
|
197
|
+
if (Array.isArray(value)) {
|
|
198
|
+
return value.map((x, idx) => _convertOutputValueFromQcsdk(comps[idx] || { type: "uint256" }, x));
|
|
199
|
+
}
|
|
200
|
+
if (value && typeof value === "object") {
|
|
201
|
+
const out = {};
|
|
202
|
+
for (let i = 0; i < comps.length; i++) {
|
|
203
|
+
const c = comps[i];
|
|
204
|
+
const name = c && typeof c.name === "string" && c.name ? c.name : String(i);
|
|
205
|
+
const raw = Object.prototype.hasOwnProperty.call(value, name)
|
|
206
|
+
? value[name]
|
|
207
|
+
: Object.prototype.hasOwnProperty.call(value, String(i))
|
|
208
|
+
? value[String(i)]
|
|
209
|
+
: undefined;
|
|
210
|
+
out[name] = _convertOutputValueFromQcsdk(c, raw);
|
|
211
|
+
}
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
return value;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const mBytes = type.match(/^bytes(\d+)$/);
|
|
218
|
+
if (mBytes) {
|
|
219
|
+
return _uint256ToFixedBytesHex(type, value);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Normalize integer outputs to bigint for consistency with core typings.
|
|
223
|
+
if (type === "uint" || type.startsWith("uint") || type === "int" || type.startsWith("int")) {
|
|
224
|
+
if (value == null) return value;
|
|
225
|
+
if (typeof value === "bigint") return value;
|
|
226
|
+
try {
|
|
227
|
+
return BigInt(value);
|
|
228
|
+
} catch {
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return value;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
class Interface {
|
|
237
|
+
/**
|
|
238
|
+
* @param {any[]|Interface} abi
|
|
239
|
+
*/
|
|
240
|
+
constructor(abi) {
|
|
241
|
+
this.abi = _asAbiArray(abi);
|
|
242
|
+
this._abiJson = JSON.stringify(this.abi);
|
|
243
|
+
this._qcsdkAbi = _normalizeAbiForQcsdk(this.abi);
|
|
244
|
+
this._qcsdkAbiJson = JSON.stringify(this._qcsdkAbi);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Returns JSON format of ABI.
|
|
249
|
+
* @returns {string}
|
|
250
|
+
*/
|
|
251
|
+
formatJson() {
|
|
252
|
+
return this._abiJson;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Internal: normalized ABI JSON for qcsdk.
|
|
257
|
+
* @returns {string}
|
|
258
|
+
*/
|
|
259
|
+
_qcsdkFormatJson() {
|
|
260
|
+
return this._qcsdkAbiJson;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Internal: normalize argument values for qcsdk based on original ABI params.
|
|
265
|
+
* @param {Array<any>} params
|
|
266
|
+
* @param {Array<any>} values
|
|
267
|
+
* @returns {Array<any>}
|
|
268
|
+
*/
|
|
269
|
+
_qcsdkNormalizeValues(params, values) {
|
|
270
|
+
const ps = Array.isArray(params) ? params : [];
|
|
271
|
+
const vs = Array.isArray(values) ? values : [];
|
|
272
|
+
return ps.map((p, i) => _convertInputValueForQcsdk(p, vs[i]));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Basic formatter (ethers supports multiple formats).
|
|
277
|
+
* @param {string=} format
|
|
278
|
+
* @returns {string}
|
|
279
|
+
*/
|
|
280
|
+
format(format) {
|
|
281
|
+
void format;
|
|
282
|
+
return this._abiJson;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get a function fragment by name (first match).
|
|
287
|
+
* @param {string} nameOrSignature
|
|
288
|
+
* @returns {FunctionFragment}
|
|
289
|
+
*/
|
|
290
|
+
getFunction(nameOrSignature) {
|
|
291
|
+
assertArgument(typeof nameOrSignature === "string", "name must be a string", "nameOrSignature", nameOrSignature);
|
|
292
|
+
const found = this.abi.find((f) => f && f.type === "function" && f.name === nameOrSignature);
|
|
293
|
+
if (!found) throw makeError("function not found", "INVALID_ARGUMENT", { nameOrSignature });
|
|
294
|
+
return new FunctionFragment(found);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get an event fragment by name (first match).
|
|
299
|
+
* @param {string} nameOrSignature
|
|
300
|
+
* @returns {EventFragment}
|
|
301
|
+
*/
|
|
302
|
+
getEvent(nameOrSignature) {
|
|
303
|
+
assertArgument(typeof nameOrSignature === "string", "name must be a string", "nameOrSignature", nameOrSignature);
|
|
304
|
+
const found = this.abi.find((f) => f && f.type === "event" && f.name === nameOrSignature);
|
|
305
|
+
if (!found) throw makeError("event not found", "INVALID_ARGUMENT", { nameOrSignature });
|
|
306
|
+
return new EventFragment(found);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Get an error fragment by name (first match).
|
|
311
|
+
* @param {string} nameOrSignature
|
|
312
|
+
* @returns {ErrorFragment}
|
|
313
|
+
*/
|
|
314
|
+
getError(nameOrSignature) {
|
|
315
|
+
assertArgument(typeof nameOrSignature === "string", "name must be a string", "nameOrSignature", nameOrSignature);
|
|
316
|
+
const found = this.abi.find((f) => f && f.type === "error" && f.name === nameOrSignature);
|
|
317
|
+
if (!found) throw makeError("error not found", "INVALID_ARGUMENT", { nameOrSignature });
|
|
318
|
+
return new ErrorFragment(found);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Returns the constructor fragment if present.
|
|
323
|
+
* @returns {ConstructorFragment|null}
|
|
324
|
+
*/
|
|
325
|
+
getConstructor() {
|
|
326
|
+
const found = this.abi.find((f) => f && f.type === "constructor");
|
|
327
|
+
return found ? new ConstructorFragment(found) : null;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Encode function data using quantum-coin-js-sdk.
|
|
332
|
+
* @param {FunctionFragment|string} functionFragment
|
|
333
|
+
* @param {any[]} values
|
|
334
|
+
* @returns {string}
|
|
335
|
+
*/
|
|
336
|
+
encodeFunctionData(functionFragment, values) {
|
|
337
|
+
_requireInitialized();
|
|
338
|
+
const name = typeof functionFragment === "string" ? functionFragment : functionFragment?.name;
|
|
339
|
+
assertArgument(typeof name === "string" && name.length > 0, "invalid function", "functionFragment", functionFragment);
|
|
340
|
+
const frag = this.getFunction(name);
|
|
341
|
+
const inputs = Array.isArray(frag.inputs) ? frag.inputs : [];
|
|
342
|
+
const rawArgs = Array.isArray(values) ? values : [];
|
|
343
|
+
|
|
344
|
+
// Fallback for complex ABI surfaces where qcsdk packing is unreliable.
|
|
345
|
+
if (jsAbi.needsJsAbi(inputs)) {
|
|
346
|
+
return jsAbi.encodeFunctionData(name, inputs, rawArgs);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const args = inputs.map((p, idx) => _convertInputValueForQcsdk(p, rawArgs[idx]));
|
|
350
|
+
const res = qcsdk.packMethodData(this._qcsdkAbiJson, name, ...args);
|
|
351
|
+
if (!res || typeof res.error !== "string") throw makeError("packMethodData failed", "UNKNOWN_ERROR", {});
|
|
352
|
+
if (res.error) throw makeError(res.error, "UNKNOWN_ERROR", { operation: "packMethodData", function: name });
|
|
353
|
+
return normalizeHex(res.result);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Decode function result using quantum-coin-js-sdk.
|
|
358
|
+
* @param {FunctionFragment|string} functionFragment
|
|
359
|
+
* @param {string} data
|
|
360
|
+
* @returns {any}
|
|
361
|
+
*/
|
|
362
|
+
decodeFunctionResult(functionFragment, data) {
|
|
363
|
+
_requireInitialized();
|
|
364
|
+
const name = typeof functionFragment === "string" ? functionFragment : functionFragment?.name;
|
|
365
|
+
assertArgument(typeof name === "string" && name.length > 0, "invalid function", "functionFragment", functionFragment);
|
|
366
|
+
assertArgument(typeof data === "string", "data must be a hex string", "data", data);
|
|
367
|
+
const frag = this.getFunction(name);
|
|
368
|
+
const outputs = Array.isArray(frag.outputs) ? frag.outputs : [];
|
|
369
|
+
|
|
370
|
+
// Fallback for complex output surfaces.
|
|
371
|
+
if (jsAbi.needsJsAbi(outputs)) {
|
|
372
|
+
return jsAbi.decodeFunctionResult(outputs, data);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const res = qcsdk.unpackMethodData(this._qcsdkAbiJson, name, data);
|
|
376
|
+
if (!res || typeof res.error !== "string") throw makeError("unpackMethodData failed", "UNKNOWN_ERROR", {});
|
|
377
|
+
if (res.error) throw makeError(res.error, "UNKNOWN_ERROR", { operation: "unpackMethodData", function: name });
|
|
378
|
+
try {
|
|
379
|
+
const decoded = JSON.parse(res.result);
|
|
380
|
+
// qcsdk generally returns an array for function outputs; convert bytesN back to hex.
|
|
381
|
+
if (Array.isArray(decoded)) {
|
|
382
|
+
return decoded.map((v, i) => _convertOutputValueFromQcsdk(outputs[i] || { type: "uint256" }, v));
|
|
383
|
+
}
|
|
384
|
+
return _convertOutputValueFromQcsdk(outputs[0] || { type: "uint256" }, decoded);
|
|
385
|
+
} catch {
|
|
386
|
+
return res.result;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Encode an event log from values.
|
|
392
|
+
* @param {EventFragment|any} eventFragment
|
|
393
|
+
* @param {any[]} values
|
|
394
|
+
* @returns {{ topics: string[], data: string }}
|
|
395
|
+
*/
|
|
396
|
+
encodeEventLog(eventFragment, values) {
|
|
397
|
+
_requireInitialized();
|
|
398
|
+
const name = typeof eventFragment === "string" ? eventFragment : eventFragment?.name;
|
|
399
|
+
assertArgument(typeof name === "string" && name.length > 0, "invalid event", "eventFragment", eventFragment);
|
|
400
|
+
const frag = this.getEvent(name);
|
|
401
|
+
const inputs = Array.isArray(frag.inputs) ? frag.inputs : [];
|
|
402
|
+
const rawArgs = Array.isArray(values) ? values : [];
|
|
403
|
+
const args = inputs.map((p, idx) => _convertInputValueForQcsdk(p, rawArgs[idx]));
|
|
404
|
+
const res = qcsdk.encodeEventLog(this._qcsdkAbiJson, name, ...args);
|
|
405
|
+
if (!res || typeof res.error !== "string") throw makeError("encodeEventLog failed", "UNKNOWN_ERROR", {});
|
|
406
|
+
if (res.error) throw makeError(res.error, "UNKNOWN_ERROR", { operation: "encodeEventLog", event: name });
|
|
407
|
+
return res.result;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Decode an event log.
|
|
412
|
+
* @param {EventFragment|any} eventFragment
|
|
413
|
+
* @param {string[]} topics
|
|
414
|
+
* @param {string} data
|
|
415
|
+
* @returns {any}
|
|
416
|
+
*/
|
|
417
|
+
decodeEventLog(eventFragment, topics, data) {
|
|
418
|
+
_requireInitialized();
|
|
419
|
+
const name = typeof eventFragment === "string" ? eventFragment : eventFragment?.name;
|
|
420
|
+
assertArgument(typeof name === "string" && name.length > 0, "invalid event", "eventFragment", eventFragment);
|
|
421
|
+
assertArgument(Array.isArray(topics), "topics must be an array", "topics", topics);
|
|
422
|
+
assertArgument(typeof data === "string", "data must be a string", "data", data);
|
|
423
|
+
const frag = this.getEvent(name);
|
|
424
|
+
const inputs = Array.isArray(frag.inputs) ? frag.inputs : [];
|
|
425
|
+
const res = qcsdk.decodeEventLog(this._qcsdkAbiJson, name, topics, data);
|
|
426
|
+
if (!res || typeof res.error !== "string") throw makeError("decodeEventLog failed", "UNKNOWN_ERROR", {});
|
|
427
|
+
if (res.error) throw makeError(res.error, "UNKNOWN_ERROR", { operation: "decodeEventLog", event: name });
|
|
428
|
+
try {
|
|
429
|
+
const decoded = JSON.parse(res.result);
|
|
430
|
+
if (Array.isArray(decoded)) {
|
|
431
|
+
return decoded.map((v, i) => _convertOutputValueFromQcsdk(inputs[i] || { type: "uint256" }, v));
|
|
432
|
+
}
|
|
433
|
+
return decoded;
|
|
434
|
+
} catch {
|
|
435
|
+
return res.result;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// The following methods exist in ethers.js v6. We provide placeholders to keep API shape.
|
|
440
|
+
parseTransaction() {
|
|
441
|
+
throw makeError("parseTransaction not implemented", "NOT_IMPLEMENTED", {});
|
|
442
|
+
}
|
|
443
|
+
parseLog() {
|
|
444
|
+
_requireInitialized();
|
|
445
|
+
const log = arguments.length > 0 ? arguments[0] : null;
|
|
446
|
+
assertArgument(log && typeof log === "object", "log must be an object", "log", log);
|
|
447
|
+
|
|
448
|
+
const topics = log.topics;
|
|
449
|
+
const data = log.data;
|
|
450
|
+
assertArgument(Array.isArray(topics), "log.topics must be an array", "log.topics", topics);
|
|
451
|
+
assertArgument(typeof data === "string", "log.data must be a string", "log.data", data);
|
|
452
|
+
assertArgument(topics.length > 0, "log.topics must contain at least 1 topic", "log.topics", topics);
|
|
453
|
+
|
|
454
|
+
const topic0 = normalizeHex(String(topics[0]));
|
|
455
|
+
|
|
456
|
+
// Find event by signature topic. (Anonymous events cannot be auto-detected.)
|
|
457
|
+
/** @type {any|null} */
|
|
458
|
+
let matched = null;
|
|
459
|
+
/** @type {string|null} */
|
|
460
|
+
let signature = null;
|
|
461
|
+
|
|
462
|
+
for (const f of this.abi) {
|
|
463
|
+
if (!f || f.type !== "event") continue;
|
|
464
|
+
if (f.anonymous) continue;
|
|
465
|
+
if (!f.name) continue;
|
|
466
|
+
const inputs = Array.isArray(f.inputs) ? f.inputs : [];
|
|
467
|
+
const sig = `${f.name}(${inputs.map((i) => String(i.type || "")).join(",")})`;
|
|
468
|
+
const t = normalizeHex(id(sig));
|
|
469
|
+
if (t === topic0) {
|
|
470
|
+
matched = f;
|
|
471
|
+
signature = sig;
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (!matched) {
|
|
477
|
+
throw makeError("no matching event for log.topics[0]", "INVALID_ARGUMENT", { topic0 });
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const fragment = new EventFragment(matched);
|
|
481
|
+
const decoded = this.decodeEventLog(fragment.name, topics.map((t) => normalizeHex(String(t))), normalizeHex(data));
|
|
482
|
+
|
|
483
|
+
const inputs = Array.isArray(matched.inputs) ? matched.inputs : [];
|
|
484
|
+
const keys = inputs.map((i) => (i && typeof i.name === "string" && i.name.length ? i.name : null));
|
|
485
|
+
|
|
486
|
+
let items = [];
|
|
487
|
+
if (Array.isArray(decoded)) {
|
|
488
|
+
items = decoded;
|
|
489
|
+
} else if (decoded && typeof decoded === "object") {
|
|
490
|
+
items = inputs.map((i, idx) => {
|
|
491
|
+
const n = i && typeof i.name === "string" ? i.name : null;
|
|
492
|
+
if (n && Object.prototype.hasOwnProperty.call(decoded, n)) return decoded[n];
|
|
493
|
+
if (Object.prototype.hasOwnProperty.call(decoded, String(idx))) return decoded[String(idx)];
|
|
494
|
+
return undefined;
|
|
495
|
+
});
|
|
496
|
+
} else {
|
|
497
|
+
items = inputs.map(() => decoded);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const args = Result.fromItems(items, keys);
|
|
501
|
+
return {
|
|
502
|
+
fragment,
|
|
503
|
+
name: fragment.name,
|
|
504
|
+
signature: signature || fragment.name,
|
|
505
|
+
topic: topic0,
|
|
506
|
+
args,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
parseError() {
|
|
510
|
+
throw makeError("parseError not implemented", "NOT_IMPLEMENTED", {});
|
|
511
|
+
}
|
|
512
|
+
getSighash() {
|
|
513
|
+
throw makeError("getSighash not implemented", "NOT_IMPLEMENTED", {});
|
|
514
|
+
}
|
|
515
|
+
getEventTopic() {
|
|
516
|
+
throw makeError("getEventTopic not implemented", "NOT_IMPLEMENTED", {});
|
|
517
|
+
}
|
|
518
|
+
getFallback() {
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
getReceive() {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
class AbiCoder {
|
|
527
|
+
/**
|
|
528
|
+
* Encode values by types into ABI data.
|
|
529
|
+
* @param {(string|any)[]} types
|
|
530
|
+
* @param {any[]} values
|
|
531
|
+
* @returns {string}
|
|
532
|
+
*/
|
|
533
|
+
encode(types, values) {
|
|
534
|
+
_requireInitialized();
|
|
535
|
+
assertArgument(Array.isArray(types), "types must be an array", "types", types);
|
|
536
|
+
assertArgument(Array.isArray(values), "values must be an array", "values", values);
|
|
537
|
+
|
|
538
|
+
const abi = [
|
|
539
|
+
{
|
|
540
|
+
type: "function",
|
|
541
|
+
name: "__encode",
|
|
542
|
+
stateMutability: "pure",
|
|
543
|
+
inputs: types.map((t, i) => ({ name: `arg${i}`, type: String(t) })),
|
|
544
|
+
outputs: [],
|
|
545
|
+
},
|
|
546
|
+
];
|
|
547
|
+
const iface = new Interface(abi);
|
|
548
|
+
const full = iface.encodeFunctionData("__encode", values);
|
|
549
|
+
// Strip the 4-byte selector (8 hex chars) + 0x
|
|
550
|
+
return "0x" + full.slice(10);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Decode ABI data by output types.
|
|
555
|
+
* @param {(string|any)[]} types
|
|
556
|
+
* @param {string} data
|
|
557
|
+
* @returns {any}
|
|
558
|
+
*/
|
|
559
|
+
decode(types, data) {
|
|
560
|
+
_requireInitialized();
|
|
561
|
+
assertArgument(Array.isArray(types), "types must be an array", "types", types);
|
|
562
|
+
assertArgument(typeof data === "string", "data must be a string", "data", data);
|
|
563
|
+
|
|
564
|
+
const abi = [
|
|
565
|
+
{
|
|
566
|
+
type: "function",
|
|
567
|
+
name: "__decode",
|
|
568
|
+
stateMutability: "pure",
|
|
569
|
+
inputs: [],
|
|
570
|
+
outputs: types.map((t, i) => ({ name: `ret${i}`, type: String(t) })),
|
|
571
|
+
},
|
|
572
|
+
];
|
|
573
|
+
const iface = new Interface(abi);
|
|
574
|
+
return iface.decodeFunctionResult("__decode", data);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Return a default value for types.
|
|
579
|
+
* @param {(string|any)[]} types
|
|
580
|
+
* @returns {any}
|
|
581
|
+
*/
|
|
582
|
+
getDefaultValue(types) {
|
|
583
|
+
// Lightweight default; ethers returns a Result. Here we return array of nulls.
|
|
584
|
+
assertArgument(Array.isArray(types), "types must be an array", "types", types);
|
|
585
|
+
return types.map(() => null);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
module.exports = { Interface, AbiCoder };
|
|
590
|
+
|