quantumcoin 8.0.1 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README-SDK.md CHANGED
@@ -610,24 +610,91 @@ ABI encoding/decoding compatibility layer.
610
610
 
611
611
  - `new Interface(abi: any[] | Interface | null)`
612
612
 
613
- **Methods**
613
+ **Fragment lookup** — each accepts a bare name, a canonical signature
614
+ (`name(type,...)`), or a hex identifier, mirroring ethers.js v6:
615
+ - `getFunction(nameOrSignatureOrSelector: string): FunctionFragment`
616
+ - Resolves by name, `transfer(address,uint256)`, or 4-byte selector `0xa9059cbb`.
617
+ - Throws `INVALID_ARGUMENT` when unresolved or when a bare name is ambiguous
618
+ across overloads (pass the full signature instead).
619
+ - `getEvent(nameOrSignatureOrTopic: string): EventFragment`
620
+ - Resolves by name, signature, or topic0 (`0x` + 64 hex).
621
+ - `getError(nameOrSignatureOrSelector: string): ErrorFragment`
622
+ - Resolves by name, signature, or 4-byte selector.
623
+ - `getConstructor(): ConstructorFragment | null`
624
+ - `getSighash(fragmentOrName): string` — 4-byte function selector.
614
625
  - `formatJson(): string`
615
626
  - `format(format?: string | null): string`
616
- - `getFunction(name: string): FunctionFragment`
617
- - `getEvent(name: string): EventFragment`
618
- - `getError(name: string): ErrorFragment`
619
- - `getConstructor(): ConstructorFragment | null`
620
627
 
621
- **Encoding**
628
+ `FunctionFragment` / `ErrorFragment` expose a `selector` getter and
629
+ `EventFragment` a `topicHash` getter (both require `Initialize()`).
630
+
631
+ **Encoding / decoding**
622
632
  - `encodeFunctionData(functionFragmentOrName, values?: any[] | null): string`
633
+ - `decodeFunctionData(functionFragmentOrName, data: string): Result` — decode a
634
+ call's arguments (selector + args). The data's selector must match the resolved
635
+ function, otherwise it throws `INVALID_ARGUMENT`. `encodeFunctionData(fragment,
636
+ decodeFunctionData(fragment, data))` reproduces `data` byte-for-byte.
623
637
  - `decodeFunctionResult(functionFragmentOrName, data: string): any`
638
+ - `encodeDeploy(values?: any[]): string` — ABI-encode constructor arguments (no
639
+ selector), or `"0x"` when the constructor takes no arguments. Append to the
640
+ creation bytecode for a deployment.
624
641
  - `encodeEventLog(eventFragmentOrName, values?: any[] | null): { topics: string[], data: string }`
625
642
  - `decodeEventLog(eventFragmentOrName, topics: string[], data: string): any`
626
643
 
627
644
  **Parsing**
645
+ - `parseTransaction(tx: { data: string, value?: BigNumberish }): { fragment, name, signature, selector, args: Result, value: bigint }`
646
+ - Resolves the function by the 4-byte selector in `tx.data`, decodes the
647
+ arguments from the real bytes, and reports `value` as a `bigint`.
648
+ - `parseError(data: string): { fragment, name, signature, selector, args: Result }`
649
+ - Resolves a custom error by its 4-byte selector and decodes its arguments.
628
650
  - `parseLog(log: { topics: string[], data: string }): { fragment, name, signature, topic, args }`
629
651
  - Uses signature topic matching and `decodeEventLog(...)`
630
652
 
653
+ > **32-byte addresses:** QuantumCoin addresses are 32 bytes and occupy a full ABI
654
+ > word, whereas Ethereum left-pads a 20-byte address. Canonical signatures are
655
+ > identical strings, so keccak-derived selectors and event topics match Ethereum
656
+ > exactly (e.g. `transfer(address,uint256)` → `0xa9059cbb`); only the contents of
657
+ > an address-bearing calldata word differ.
658
+
659
+ **Example (JS):**
660
+
661
+ ```js
662
+ const { Initialize } = require("quantumcoin/config");
663
+ const { Interface } = require("quantumcoin");
664
+
665
+ await Initialize(null);
666
+ const iface = new Interface([
667
+ { type: "function", name: "transfer",
668
+ inputs: [{ name: "to", type: "address" }, { name: "amount", type: "uint256" }],
669
+ outputs: [{ type: "bool" }] },
670
+ ]);
671
+
672
+ const to = "0x" + "ab".repeat(32); // 32-byte address
673
+ const data = iface.encodeFunctionData("transfer", [to, 1000n]);
674
+
675
+ const tx = iface.parseTransaction({ data, value: "0x0" });
676
+ console.log(tx.name, tx.signature, tx.selector); // transfer transfer(address,uint256) 0xa9059cbb
677
+ console.log(tx.args.to, tx.args.amount); // <to> 1000n
678
+
679
+ // "What you see is what you sign": re-encode the decoded args and compare.
680
+ const ok = iface.encodeFunctionData(tx.fragment, Array.from(tx.args)) === data; // true
681
+ ```
682
+
683
+ **Example (TS):**
684
+
685
+ ```ts
686
+ import { Initialize } from "quantumcoin/config";
687
+ import qc from "quantumcoin";
688
+
689
+ await Initialize(null);
690
+ const iface = new qc.Interface([
691
+ { type: "error", name: "InsufficientBalance",
692
+ inputs: [{ name: "have", type: "uint256" }, { name: "want", type: "uint256" }] },
693
+ ]);
694
+ const selector: string = iface.getError("InsufficientBalance").selector;
695
+ // const desc = iface.parseError(revertData); // { name, args: { have, want }, ... }
696
+ ```
697
+
631
698
  ### `AbiCoder`
632
699
 
633
700
  Minimal ABI coder for encoding/decoding tuples of values.
@@ -635,6 +702,8 @@ Minimal ABI coder for encoding/decoding tuples of values.
635
702
  - `encode(types: (string|any)[], values: any[]): string`
636
703
  - `decode(types: (string|any)[], data: string): any`
637
704
  - `getDefaultValue(types: (string|any)[]): any`
705
+ - `static defaultAbiCoder(): AbiCoder` — shared singleton instance (ethers.js v6
706
+ shape): `AbiCoder.defaultAbiCoder().encode(["uint256"], [1n])`.
638
707
 
639
708
  ## Utilities
640
709
 
package/README.md CHANGED
@@ -148,6 +148,48 @@ const staking = new Contract(address, abi, provider);
148
148
  console.log(await staking.getDepositorCount());
149
149
  ```
150
150
 
151
+ ## ABI encoding & decoding
152
+
153
+ `Interface` and `AbiCoder` provide ethers.js v6-compatible ABI handling. Beyond
154
+ encoding, you can resolve fragments by name, canonical signature, or hex
155
+ identifier, and decode/parse raw calldata — useful for a strict "what you see is
156
+ what you sign" verification (decode the real bytes, then re-encode and compare).
157
+
158
+ ```js
159
+ const { Initialize } = require("quantumcoin/config");
160
+ const { Interface, AbiCoder } = require("quantumcoin");
161
+
162
+ await Initialize(null);
163
+ const iface = new Interface([
164
+ { type: "function", name: "transfer",
165
+ inputs: [{ name: "to", type: "address" }, { name: "amount", type: "uint256" }],
166
+ outputs: [{ type: "bool" }] },
167
+ ]);
168
+
169
+ const to = "0x" + "ab".repeat(32); // 32-byte QuantumCoin address
170
+ const data = iface.encodeFunctionData("transfer", [to, 1000n]);
171
+
172
+ const tx = iface.parseTransaction({ data }); // resolves by selector, decodes args
173
+ // tx.name === "transfer", tx.selector === "0xa9059cbb", tx.args.amount === 1000n
174
+
175
+ // Re-encode the decoded args and confirm they reproduce the original calldata.
176
+ const verified = iface.encodeFunctionData(tx.fragment, Array.from(tx.args)) === data;
177
+
178
+ const coder = AbiCoder.defaultAbiCoder();
179
+ coder.encode(["uint256"], [1n]);
180
+ ```
181
+
182
+ Highlights: `getFunction`/`getError` (by name, signature, or 4-byte selector),
183
+ `getEvent` (by name, signature, or topic0), `decodeFunctionData`,
184
+ `parseTransaction`, `parseError`, `getSighash`, `FunctionFragment.selector`,
185
+ `EventFragment.topicHash`, `Interface.encodeDeploy`, and
186
+ `AbiCoder.defaultAbiCoder()`.
187
+
188
+ > QuantumCoin addresses are 32 bytes and fill a full ABI word (Ethereum left-pads
189
+ > a 20-byte address). Canonical signatures are identical, so selectors and event
190
+ > topics match Ethereum exactly; only address-bearing calldata words differ. See
191
+ > [README-SDK.md](./README-SDK.md#interface) for the full API.
192
+
151
193
  ## Typed contract generator
152
194
 
153
195
  This repo includes a generator described in `SPEC.md` section 15.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quantumcoin",
3
- "version": "8.0.1",
3
+ "version": "8.0.2",
4
4
  "description": "QuantumCoin.js - a post quantum cryptography SDK for QuantumCoin",
5
5
  "main": "index.js",
6
6
  "types": "src/index.d.ts",
@@ -29,10 +29,28 @@ export class Fragment {
29
29
  export class NamedFragment extends Fragment {
30
30
  }
31
31
  export class FunctionFragment extends NamedFragment {
32
+ /**
33
+ * The 4-byte function selector (sighash), e.g. "0xa9059cbb". Requires the SDK
34
+ * to be initialized (uses keccak256). Mirrors ethers.js v6.
35
+ * @returns {string}
36
+ */
37
+ get selector(): string;
32
38
  }
33
39
  export class EventFragment extends NamedFragment {
40
+ /**
41
+ * The event topic hash (topic0). Requires the SDK to be initialized (uses
42
+ * keccak256). Mirrors ethers.js v6.
43
+ * @returns {string}
44
+ */
45
+ get topicHash(): string;
34
46
  }
35
47
  export class ErrorFragment extends NamedFragment {
48
+ /**
49
+ * The 4-byte error selector. Requires the SDK to be initialized (uses
50
+ * keccak256). Mirrors ethers.js v6.
51
+ * @returns {string}
52
+ */
53
+ get selector(): string;
36
54
  }
37
55
  export class ConstructorFragment extends Fragment {
38
56
  }
@@ -43,9 +43,52 @@ class Fragment {
43
43
  }
44
44
 
45
45
  class NamedFragment extends Fragment {}
46
- class FunctionFragment extends NamedFragment {}
47
- class EventFragment extends NamedFragment {}
48
- class ErrorFragment extends NamedFragment {}
46
+
47
+ class FunctionFragment extends NamedFragment {
48
+ /**
49
+ * The 4-byte function selector (sighash), e.g. "0xa9059cbb". Requires the SDK
50
+ * to be initialized (uses keccak256). Mirrors ethers.js v6.
51
+ * @returns {string}
52
+ */
53
+ get selector() {
54
+ // Lazy require to keep fragment construction dependency-free.
55
+ // eslint-disable-next-line global-require
56
+ const { functionSelectorHex } = require("./js-abi-coder");
57
+ return functionSelectorHex(this.name, Array.isArray(this.inputs) ? this.inputs : []);
58
+ }
59
+ }
60
+
61
+ class EventFragment extends NamedFragment {
62
+ /**
63
+ * The event topic hash (topic0). Requires the SDK to be initialized (uses
64
+ * keccak256). Mirrors ethers.js v6.
65
+ * @returns {string}
66
+ */
67
+ get topicHash() {
68
+ // eslint-disable-next-line global-require
69
+ const { canonicalType } = require("./js-abi-coder");
70
+ // eslint-disable-next-line global-require
71
+ const { id } = require("../utils/hashing");
72
+ // eslint-disable-next-line global-require
73
+ const { normalizeHex } = require("../internal/hex");
74
+ const inputs = Array.isArray(this.inputs) ? this.inputs : [];
75
+ return normalizeHex(id(`${this.name}(${inputs.map((i) => canonicalType(i)).join(",")})`));
76
+ }
77
+ }
78
+
79
+ class ErrorFragment extends NamedFragment {
80
+ /**
81
+ * The 4-byte error selector. Requires the SDK to be initialized (uses
82
+ * keccak256). Mirrors ethers.js v6.
83
+ * @returns {string}
84
+ */
85
+ get selector() {
86
+ // eslint-disable-next-line global-require
87
+ const { functionSelectorHex } = require("./js-abi-coder");
88
+ return functionSelectorHex(this.name, Array.isArray(this.inputs) ? this.inputs : []);
89
+ }
90
+ }
91
+
49
92
  class ConstructorFragment extends Fragment {}
50
93
  class StructFragment extends Fragment {}
51
94
  class FallbackFragment extends Fragment {}
@@ -31,23 +31,26 @@ export class Interface {
31
31
  */
32
32
  format(format?: string | undefined): string;
33
33
  /**
34
- * Get a function fragment by name (first match).
35
- * @param {string} nameOrSignature
34
+ * Get a function fragment by bare name, canonical signature
35
+ * (`name(type,...)`), or 4-byte selector (`0x` + 8 hex). Mirrors ethers.js v6.
36
+ * @param {string} nameOrSignatureOrSelector
36
37
  * @returns {FunctionFragment}
37
38
  */
38
- getFunction(nameOrSignature: string): FunctionFragment;
39
+ getFunction(nameOrSignatureOrSelector: string): FunctionFragment;
39
40
  /**
40
- * Get an event fragment by name (first match).
41
- * @param {string} nameOrSignature
41
+ * Get an event fragment by bare name, canonical signature, or topic0
42
+ * (`0x` + 64 hex). Mirrors ethers.js v6.
43
+ * @param {string} nameOrSignatureOrTopic
42
44
  * @returns {EventFragment}
43
45
  */
44
- getEvent(nameOrSignature: string): EventFragment;
46
+ getEvent(nameOrSignatureOrTopic: string): EventFragment;
45
47
  /**
46
- * Get an error fragment by name (first match).
47
- * @param {string} nameOrSignature
48
+ * Get an error fragment by bare name, canonical signature, or 4-byte selector.
49
+ * Mirrors ethers.js v6.
50
+ * @param {string} nameOrSignatureOrSelector
48
51
  * @returns {ErrorFragment}
49
52
  */
50
- getError(nameOrSignature: string): ErrorFragment;
53
+ getError(nameOrSignatureOrSelector: string): ErrorFragment;
51
54
  /**
52
55
  * Returns the constructor fragment if present.
53
56
  * @returns {ConstructorFragment|null}
@@ -60,6 +63,23 @@ export class Interface {
60
63
  * @returns {string}
61
64
  */
62
65
  encodeFunctionData(functionFragment: FunctionFragment | string, values: any[]): string;
66
+ /**
67
+ * Encode ABI constructor arguments for a contract deployment (no selector).
68
+ * Returns "0x" when the constructor takes no arguments. The caller appends
69
+ * this to the creation bytecode. Mirrors ethers.js v6.
70
+ * @param {any[]=} values
71
+ * @returns {string}
72
+ */
73
+ encodeDeploy(values?: any[] | undefined): string;
74
+ /**
75
+ * Decode the calldata of a function call (4-byte selector + ABI-encoded
76
+ * arguments), returning the decoded input arguments as a Result. The data's
77
+ * selector must match the resolved function. Mirrors ethers.js v6.
78
+ * @param {FunctionFragment|string} fragment
79
+ * @param {string} data
80
+ * @returns {Result}
81
+ */
82
+ decodeFunctionData(fragment: FunctionFragment | string, data: string): Result;
63
83
  /**
64
84
  * Decode function result using quantum-coin-js-sdk.
65
85
  * @param {FunctionFragment|string} functionFragment
@@ -85,7 +105,25 @@ export class Interface {
85
105
  * @returns {any}
86
106
  */
87
107
  decodeEventLog(eventFragment: EventFragment | any, topics: string[], data: string): any;
88
- parseTransaction(): void;
108
+ /**
109
+ * Parse a transaction's calldata into its function + decoded arguments.
110
+ * Resolves the function by the 4-byte selector in `tx.data`, decodes the
111
+ * arguments from the real bytes, and reports `value` as a bigint. Mirrors
112
+ * ethers.js v6 (returns a TransactionDescription-shaped object).
113
+ * @param {{ data: string, value?: any }} tx
114
+ * @returns {{ fragment: FunctionFragment, name: string, signature: string, selector: string, args: Result, value: bigint }}
115
+ */
116
+ parseTransaction(tx: {
117
+ data: string;
118
+ value?: any;
119
+ }): {
120
+ fragment: FunctionFragment;
121
+ name: string;
122
+ signature: string;
123
+ selector: string;
124
+ args: Result;
125
+ value: bigint;
126
+ };
89
127
  parseLog(...args: any[]): {
90
128
  fragment: EventFragment;
91
129
  name: any;
@@ -93,8 +131,27 @@ export class Interface {
93
131
  topic: string;
94
132
  args: Result;
95
133
  };
96
- parseError(): void;
97
- getSighash(): void;
134
+ /**
135
+ * Parse custom-error return data into its error fragment + decoded arguments.
136
+ * Resolves the error by the 4-byte selector in `data`. Mirrors ethers.js v6
137
+ * (returns an ErrorDescription-shaped object).
138
+ * @param {string} data
139
+ * @returns {{ fragment: ErrorFragment, name: string, signature: string, selector: string, args: Result }}
140
+ */
141
+ parseError(data: string): {
142
+ fragment: ErrorFragment;
143
+ name: string;
144
+ signature: string;
145
+ selector: string;
146
+ args: Result;
147
+ };
148
+ /**
149
+ * Return the 4-byte function selector (sighash) for a function fragment or
150
+ * name. Mirrors ethers.js v5 getSighash / v6 FunctionFragment.selector.
151
+ * @param {FunctionFragment|string} fragmentOrName
152
+ * @returns {string}
153
+ */
154
+ getSighash(fragmentOrName: FunctionFragment | string): string;
98
155
  /**
99
156
  * Compute the topic0 (event signature hash) for an event.
100
157
  * @param {string|EventFragment|any} nameOrFragment
@@ -105,6 +162,11 @@ export class Interface {
105
162
  getReceive(): null;
106
163
  }
107
164
  export class AbiCoder {
165
+ /**
166
+ * Return the shared default AbiCoder instance (ethers.js v6 shape).
167
+ * @returns {AbiCoder}
168
+ */
169
+ static defaultAbiCoder(): AbiCoder;
108
170
  /**
109
171
  * Encode values by types into ABI data.
110
172
  * @param {(string|any)[]} types
@@ -8,12 +8,45 @@
8
8
 
9
9
  const qcsdk = require("quantum-coin-js-sdk");
10
10
  const { makeError, assertArgument } = require("../errors");
11
- const { normalizeHex, arrayify, bytesToHex } = require("../internal/hex");
11
+ const { normalizeHex, arrayify, bytesToHex, isHexString } = require("../internal/hex");
12
12
  const { EventFragment, FunctionFragment, ErrorFragment, ConstructorFragment } = require("./fragments");
13
13
  const { Result } = require("../utils/result");
14
14
  const { id } = require("../utils/hashing");
15
15
  const jsAbi = require("./js-abi-coder");
16
16
 
17
+ // Remove all whitespace (used to normalize a caller-supplied canonical signature
18
+ // like "transfer(address, uint256)" before comparing against the ABI).
19
+ function _stripWhitespace(s) {
20
+ return String(s).replace(/\s+/g, "");
21
+ }
22
+
23
+ // Canonical Solidity signature for a fragment, e.g. "transfer(address,uint256)".
24
+ // Tuples/arrays are expanded via the shared js-abi-coder canonicalizer so it
25
+ // matches the selector/topic hashing used everywhere else.
26
+ function _canonicalSignature(frag) {
27
+ const inputs = Array.isArray(frag.inputs) ? frag.inputs : [];
28
+ return `${frag.name}(${inputs.map((i) => jsAbi.canonicalType(i)).join(",")})`;
29
+ }
30
+
31
+ // 0x + 8 hex => a 4-byte function/error selector.
32
+ function _isSelectorHex(s) {
33
+ return typeof s === "string" && /^0x[0-9a-fA-F]{8}$/.test(s.trim());
34
+ }
35
+
36
+ // 0x + 64 hex => a 32-byte event topic (topic0).
37
+ function _isTopicHex(s) {
38
+ return typeof s === "string" && /^0x[0-9a-fA-F]{64}$/.test(s.trim());
39
+ }
40
+
41
+ // Resolve a function fragment for decode/encode. A fragment object (e.g. the one
42
+ // returned by parseTransaction/getFunction) is used AS-IS so an overloaded name
43
+ // is never re-resolved ambiguously; a string resolves through the ABI.
44
+ function _resolveFunctionFragment(iface, fragment) {
45
+ if (fragment instanceof FunctionFragment) return fragment;
46
+ if (fragment && typeof fragment === "object" && fragment.name) return new FunctionFragment(fragment);
47
+ return iface.getFunction(fragment);
48
+ }
49
+
17
50
  function _requireInitialized() {
18
51
  // eslint-disable-next-line global-require
19
52
  const { isInitialized, getInitializationPromise } = require("../../config");
@@ -289,39 +322,96 @@ class Interface {
289
322
  }
290
323
 
291
324
  /**
292
- * Get a function fragment by name (first match).
293
- * @param {string} nameOrSignature
325
+ * Get a function fragment by bare name, canonical signature
326
+ * (`name(type,...)`), or 4-byte selector (`0x` + 8 hex). Mirrors ethers.js v6.
327
+ * @param {string} nameOrSignatureOrSelector
294
328
  * @returns {FunctionFragment}
295
329
  */
296
- getFunction(nameOrSignature) {
297
- assertArgument(typeof nameOrSignature === "string", "name must be a string", "nameOrSignature", nameOrSignature);
298
- const found = this.abi.find((f) => f && f.type === "function" && f.name === nameOrSignature);
299
- if (!found) throw makeError("function not found", "INVALID_ARGUMENT", { nameOrSignature });
300
- return new FunctionFragment(found);
330
+ getFunction(nameOrSignatureOrSelector) {
331
+ assertArgument(typeof nameOrSignatureOrSelector === "string", "name must be a string", "nameOrSignatureOrSelector", nameOrSignatureOrSelector);
332
+ const key = nameOrSignatureOrSelector.trim();
333
+ const fns = this.abi.filter((f) => f && f.type === "function" && f.name);
334
+
335
+ if (_isSelectorHex(key)) {
336
+ const sel = normalizeHex(key).toLowerCase();
337
+ const found = fns.find((f) => jsAbi.functionSelectorHex(f.name, f.inputs || []).toLowerCase() === sel);
338
+ if (!found) throw makeError("no matching function for selector", "INVALID_ARGUMENT", { selector: key });
339
+ return new FunctionFragment(found);
340
+ }
341
+
342
+ if (key.indexOf("(") >= 0) {
343
+ const norm = _stripWhitespace(key);
344
+ const found = fns.find((f) => _canonicalSignature(f) === norm);
345
+ if (!found) throw makeError("no matching function for signature", "INVALID_ARGUMENT", { signature: key });
346
+ return new FunctionFragment(found);
347
+ }
348
+
349
+ const matches = fns.filter((f) => f.name === key);
350
+ if (matches.length === 0) throw makeError("function not found", "INVALID_ARGUMENT", { nameOrSignatureOrSelector });
351
+ if (matches.length > 1) {
352
+ throw makeError("ambiguous function name; specify the full signature", "INVALID_ARGUMENT", { nameOrSignatureOrSelector });
353
+ }
354
+ return new FunctionFragment(matches[0]);
301
355
  }
302
356
 
303
357
  /**
304
- * Get an event fragment by name (first match).
305
- * @param {string} nameOrSignature
358
+ * Get an event fragment by bare name, canonical signature, or topic0
359
+ * (`0x` + 64 hex). Mirrors ethers.js v6.
360
+ * @param {string} nameOrSignatureOrTopic
306
361
  * @returns {EventFragment}
307
362
  */
308
- getEvent(nameOrSignature) {
309
- assertArgument(typeof nameOrSignature === "string", "name must be a string", "nameOrSignature", nameOrSignature);
310
- const found = this.abi.find((f) => f && f.type === "event" && f.name === nameOrSignature);
311
- if (!found) throw makeError("event not found", "INVALID_ARGUMENT", { nameOrSignature });
312
- return new EventFragment(found);
363
+ getEvent(nameOrSignatureOrTopic) {
364
+ assertArgument(typeof nameOrSignatureOrTopic === "string", "name must be a string", "nameOrSignatureOrTopic", nameOrSignatureOrTopic);
365
+ const key = nameOrSignatureOrTopic.trim();
366
+ const evs = this.abi.filter((f) => f && f.type === "event" && f.name);
367
+
368
+ if (_isTopicHex(key)) {
369
+ const topic = normalizeHex(key);
370
+ const found = evs.find((f) => !f.anonymous && normalizeHex(id(_canonicalSignature(f))) === topic);
371
+ if (!found) throw makeError("no matching event for topic", "INVALID_ARGUMENT", { topic: key });
372
+ return new EventFragment(found);
373
+ }
374
+
375
+ if (key.indexOf("(") >= 0) {
376
+ const norm = _stripWhitespace(key);
377
+ const found = evs.find((f) => _canonicalSignature(f) === norm);
378
+ if (!found) throw makeError("no matching event for signature", "INVALID_ARGUMENT", { signature: key });
379
+ return new EventFragment(found);
380
+ }
381
+
382
+ const matches = evs.filter((f) => f.name === key);
383
+ if (matches.length === 0) throw makeError("event not found", "INVALID_ARGUMENT", { nameOrSignatureOrTopic });
384
+ return new EventFragment(matches[0]);
313
385
  }
314
386
 
315
387
  /**
316
- * Get an error fragment by name (first match).
317
- * @param {string} nameOrSignature
388
+ * Get an error fragment by bare name, canonical signature, or 4-byte selector.
389
+ * Mirrors ethers.js v6.
390
+ * @param {string} nameOrSignatureOrSelector
318
391
  * @returns {ErrorFragment}
319
392
  */
320
- getError(nameOrSignature) {
321
- assertArgument(typeof nameOrSignature === "string", "name must be a string", "nameOrSignature", nameOrSignature);
322
- const found = this.abi.find((f) => f && f.type === "error" && f.name === nameOrSignature);
323
- if (!found) throw makeError("error not found", "INVALID_ARGUMENT", { nameOrSignature });
324
- return new ErrorFragment(found);
393
+ getError(nameOrSignatureOrSelector) {
394
+ assertArgument(typeof nameOrSignatureOrSelector === "string", "name must be a string", "nameOrSignatureOrSelector", nameOrSignatureOrSelector);
395
+ const key = nameOrSignatureOrSelector.trim();
396
+ const errs = this.abi.filter((f) => f && f.type === "error" && f.name);
397
+
398
+ if (_isSelectorHex(key)) {
399
+ const sel = normalizeHex(key).toLowerCase();
400
+ const found = errs.find((f) => jsAbi.functionSelectorHex(f.name, f.inputs || []).toLowerCase() === sel);
401
+ if (!found) throw makeError("no matching error for selector", "INVALID_ARGUMENT", { selector: key });
402
+ return new ErrorFragment(found);
403
+ }
404
+
405
+ if (key.indexOf("(") >= 0) {
406
+ const norm = _stripWhitespace(key);
407
+ const found = errs.find((f) => _canonicalSignature(f) === norm);
408
+ if (!found) throw makeError("no matching error for signature", "INVALID_ARGUMENT", { signature: key });
409
+ return new ErrorFragment(found);
410
+ }
411
+
412
+ const matches = errs.filter((f) => f.name === key);
413
+ if (matches.length === 0) throw makeError("error not found", "INVALID_ARGUMENT", { nameOrSignatureOrSelector });
414
+ return new ErrorFragment(matches[0]);
325
415
  }
326
416
 
327
417
  /**
@@ -341,24 +431,76 @@ class Interface {
341
431
  */
342
432
  encodeFunctionData(functionFragment, values) {
343
433
  _requireInitialized();
344
- const name = typeof functionFragment === "string" ? functionFragment : functionFragment?.name;
434
+ const rawArgs = Array.isArray(values) ? values : [];
435
+
436
+ // When a fragment object is supplied (e.g. from parseTransaction /
437
+ // getFunction), encode directly from its exact inputs via the pure-JS coder.
438
+ // This guarantees encode/decode is a faithful round-trip (including
439
+ // overloaded functions) and matches decodeFunctionData byte-for-byte.
440
+ if (functionFragment && typeof functionFragment === "object") {
441
+ const fname = functionFragment.name;
442
+ assertArgument(typeof fname === "string" && fname.length > 0, "invalid function", "functionFragment", functionFragment);
443
+ const finputs = Array.isArray(functionFragment.inputs) ? functionFragment.inputs : [];
444
+ return jsAbi.encodeFunctionData(fname, finputs, rawArgs);
445
+ }
446
+
447
+ const name = functionFragment;
345
448
  assertArgument(typeof name === "string" && name.length > 0, "invalid function", "functionFragment", functionFragment);
346
449
  const frag = this.getFunction(name);
347
450
  const inputs = Array.isArray(frag.inputs) ? frag.inputs : [];
348
- const rawArgs = Array.isArray(values) ? values : [];
349
451
 
350
452
  // Fallback for complex ABI surfaces where qcsdk packing is unreliable.
351
453
  if (jsAbi.needsJsAbi(inputs)) {
352
- return jsAbi.encodeFunctionData(name, inputs, rawArgs);
454
+ return jsAbi.encodeFunctionData(frag.name, inputs, rawArgs);
353
455
  }
354
456
 
355
457
  const args = inputs.map((p, idx) => _convertInputValueForQcsdk(p, rawArgs[idx]));
356
- const res = qcsdk.packMethodData(this._qcsdkAbiJson, name, ...args);
458
+ const res = qcsdk.packMethodData(this._qcsdkAbiJson, frag.name, ...args);
357
459
  if (!res || typeof res.error !== "string") throw makeError("packMethodData failed", "UNKNOWN_ERROR", {});
358
- if (res.error) throw makeError(res.error, "UNKNOWN_ERROR", { operation: "packMethodData", function: name });
460
+ if (res.error) throw makeError(res.error, "UNKNOWN_ERROR", { operation: "packMethodData", function: frag.name });
359
461
  return normalizeHex(res.result);
360
462
  }
361
463
 
464
+ /**
465
+ * Encode ABI constructor arguments for a contract deployment (no selector).
466
+ * Returns "0x" when the constructor takes no arguments. The caller appends
467
+ * this to the creation bytecode. Mirrors ethers.js v6.
468
+ * @param {any[]=} values
469
+ * @returns {string}
470
+ */
471
+ encodeDeploy(values) {
472
+ _requireInitialized();
473
+ const ctor = this.getConstructor();
474
+ const inputs = ctor && Array.isArray(ctor.inputs) ? ctor.inputs : [];
475
+ const rawArgs = Array.isArray(values) ? values : [];
476
+ if (inputs.length === 0) return "0x";
477
+ return normalizeHex(bytesToHex(jsAbi.encodeTupleLike(inputs, rawArgs)));
478
+ }
479
+
480
+ /**
481
+ * Decode the calldata of a function call (4-byte selector + ABI-encoded
482
+ * arguments), returning the decoded input arguments as a Result. The data's
483
+ * selector must match the resolved function. Mirrors ethers.js v6.
484
+ * @param {FunctionFragment|string} fragment
485
+ * @param {string} data
486
+ * @returns {Result}
487
+ */
488
+ decodeFunctionData(fragment, data) {
489
+ _requireInitialized();
490
+ assertArgument(typeof data === "string" && isHexString(data), "data must be a hex string", "data", data);
491
+ const frag = _resolveFunctionFragment(this, fragment);
492
+ const inputs = Array.isArray(frag.inputs) ? frag.inputs : [];
493
+ const selector = jsAbi.functionSelectorHex(frag.name, inputs).toLowerCase();
494
+ const norm = normalizeHex(data);
495
+ const bytes = arrayify(norm);
496
+ if (bytes.length < 4 || norm.slice(0, 10).toLowerCase() !== selector) {
497
+ throw makeError("data selector does not match function", "INVALID_ARGUMENT", { function: frag.name, data });
498
+ }
499
+ const items = jsAbi.decodeTupleLike(inputs, bytes.slice(4), 0);
500
+ const keys = inputs.map((i) => (i && typeof i.name === "string" && i.name.length ? i.name : null));
501
+ return Result.fromItems(items, keys);
502
+ }
503
+
362
504
  /**
363
505
  * Decode function result using quantum-coin-js-sdk.
364
506
  * @param {FunctionFragment|string} functionFragment
@@ -442,9 +584,40 @@ class Interface {
442
584
  }
443
585
  }
444
586
 
445
- // The following methods exist in ethers.js v6. We provide placeholders to keep API shape.
446
- parseTransaction() {
447
- throw makeError("parseTransaction not implemented", "NOT_IMPLEMENTED", {});
587
+ /**
588
+ * Parse a transaction's calldata into its function + decoded arguments.
589
+ * Resolves the function by the 4-byte selector in `tx.data`, decodes the
590
+ * arguments from the real bytes, and reports `value` as a bigint. Mirrors
591
+ * ethers.js v6 (returns a TransactionDescription-shaped object).
592
+ * @param {{ data: string, value?: any }} tx
593
+ * @returns {{ fragment: FunctionFragment, name: string, signature: string, selector: string, args: Result, value: bigint }}
594
+ */
595
+ parseTransaction(tx) {
596
+ _requireInitialized();
597
+ assertArgument(tx && typeof tx === "object", "tx must be an object", "tx", tx);
598
+ const data = tx.data;
599
+ assertArgument(typeof data === "string" && isHexString(data), "tx.data must be a hex string", "tx.data", data);
600
+ const norm = normalizeHex(data);
601
+ if (arrayify(norm).length < 4) throw makeError("data too short for a function selector", "INVALID_ARGUMENT", { data });
602
+ const selector = norm.slice(0, 10);
603
+ const frag = this.getFunction(selector);
604
+ const args = this.decodeFunctionData(frag, norm);
605
+ let value = 0n;
606
+ if (tx.value != null) {
607
+ try {
608
+ value = BigInt(tx.value);
609
+ } catch {
610
+ value = 0n;
611
+ }
612
+ }
613
+ return {
614
+ fragment: frag,
615
+ name: frag.name,
616
+ signature: _canonicalSignature(frag),
617
+ selector: jsAbi.functionSelectorHex(frag.name, Array.isArray(frag.inputs) ? frag.inputs : []),
618
+ args,
619
+ value,
620
+ };
448
621
  }
449
622
  parseLog() {
450
623
  _requireInitialized();
@@ -512,11 +685,47 @@ class Interface {
512
685
  args,
513
686
  };
514
687
  }
515
- parseError() {
516
- throw makeError("parseError not implemented", "NOT_IMPLEMENTED", {});
688
+ /**
689
+ * Parse custom-error return data into its error fragment + decoded arguments.
690
+ * Resolves the error by the 4-byte selector in `data`. Mirrors ethers.js v6
691
+ * (returns an ErrorDescription-shaped object).
692
+ * @param {string} data
693
+ * @returns {{ fragment: ErrorFragment, name: string, signature: string, selector: string, args: Result }}
694
+ */
695
+ parseError(data) {
696
+ _requireInitialized();
697
+ assertArgument(typeof data === "string" && isHexString(data), "data must be a hex string", "data", data);
698
+ const norm = normalizeHex(data);
699
+ const bytes = arrayify(norm);
700
+ if (bytes.length < 4) throw makeError("data too short for an error selector", "INVALID_ARGUMENT", { data });
701
+ const selector = norm.slice(0, 10).toLowerCase();
702
+ const errs = this.abi.filter((f) => f && f.type === "error" && f.name);
703
+ const found = errs.find((f) => jsAbi.functionSelectorHex(f.name, f.inputs || []).toLowerCase() === selector);
704
+ if (!found) throw makeError("no matching error for selector", "INVALID_ARGUMENT", { selector });
705
+ const inputs = Array.isArray(found.inputs) ? found.inputs : [];
706
+ const items = jsAbi.decodeTupleLike(inputs, bytes.slice(4), 0);
707
+ const keys = inputs.map((i) => (i && typeof i.name === "string" && i.name.length ? i.name : null));
708
+ return {
709
+ fragment: new ErrorFragment(found),
710
+ name: found.name,
711
+ signature: _canonicalSignature(found),
712
+ selector: jsAbi.functionSelectorHex(found.name, inputs),
713
+ args: Result.fromItems(items, keys),
714
+ };
517
715
  }
518
- getSighash() {
519
- throw makeError("getSighash not implemented", "NOT_IMPLEMENTED", {});
716
+
717
+ /**
718
+ * Return the 4-byte function selector (sighash) for a function fragment or
719
+ * name. Mirrors ethers.js v5 getSighash / v6 FunctionFragment.selector.
720
+ * @param {FunctionFragment|string} fragmentOrName
721
+ * @returns {string}
722
+ */
723
+ getSighash(fragmentOrName) {
724
+ const frag =
725
+ fragmentOrName && typeof fragmentOrName === "object" && fragmentOrName.name
726
+ ? fragmentOrName
727
+ : this.getFunction(fragmentOrName);
728
+ return jsAbi.functionSelectorHex(frag.name, Array.isArray(frag.inputs) ? frag.inputs : []);
520
729
  }
521
730
  /**
522
731
  * Compute the topic0 (event signature hash) for an event.
@@ -599,6 +808,15 @@ class AbiCoder {
599
808
  assertArgument(Array.isArray(types), "types must be an array", "types", types);
600
809
  return types.map(() => null);
601
810
  }
811
+
812
+ /**
813
+ * Return the shared default AbiCoder instance (ethers.js v6 shape).
814
+ * @returns {AbiCoder}
815
+ */
816
+ static defaultAbiCoder() {
817
+ if (!AbiCoder._instance) AbiCoder._instance = new AbiCoder();
818
+ return AbiCoder._instance;
819
+ }
602
820
  }
603
821
 
604
822
  module.exports = { Interface, AbiCoder };
@@ -6,3 +6,5 @@ export function functionSelectorHex(name: any, inputs: any): string;
6
6
  export function encodeFunctionData(name: any, inputs: any, values: any): string;
7
7
  export function encodeTupleLike(params: any, values: any, depth: any): Uint8Array<any>;
8
8
  export function decodeFunctionResult(outputs: any, dataHex: any): any[];
9
+ export function decodeTupleLike(params: any, data: any, baseOffset: any, depth: any): any[];
10
+ export function decodeParam(param: any, data: any, offset: any, depth: any): any;
@@ -534,5 +534,7 @@ module.exports = {
534
534
  encodeFunctionData,
535
535
  encodeTupleLike,
536
536
  decodeFunctionResult,
537
+ decodeTupleLike,
538
+ decodeParam,
537
539
  };
538
540