@hashgraphonline/standards-sdk 0.1.184 → 0.1.185
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/dist/browser/hcs-25/identifiers.d.ts +24 -0
- package/dist/browser/hcs-25/identifiers.d.ts.map +1 -0
- package/dist/browser/hcs-25/index.d.ts +4 -0
- package/dist/browser/hcs-25/index.d.ts.map +1 -0
- package/dist/browser/hcs-25/scoring.d.ts +32 -0
- package/dist/browser/hcs-25/scoring.d.ts.map +1 -0
- package/dist/browser/hcs-25/types.d.ts +363 -0
- package/dist/browser/hcs-25/types.d.ts.map +1 -0
- package/dist/browser/index.d.ts +1 -0
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser-root/hcs-25/identifiers.d.ts +24 -0
- package/dist/browser-root/hcs-25/identifiers.d.ts.map +1 -0
- package/dist/browser-root/hcs-25/index.d.ts +4 -0
- package/dist/browser-root/hcs-25/index.d.ts.map +1 -0
- package/dist/browser-root/hcs-25/scoring.d.ts +32 -0
- package/dist/browser-root/hcs-25/scoring.d.ts.map +1 -0
- package/dist/browser-root/hcs-25/types.d.ts +363 -0
- package/dist/browser-root/hcs-25/types.d.ts.map +1 -0
- package/dist/browser-root/index.d.ts +1 -0
- package/dist/browser-root/index.d.ts.map +1 -1
- package/dist/cjs/hcs-25/identifiers.d.ts +24 -0
- package/dist/cjs/hcs-25/identifiers.d.ts.map +1 -0
- package/dist/cjs/hcs-25/index.d.ts +4 -0
- package/dist/cjs/hcs-25/index.d.ts.map +1 -0
- package/dist/cjs/hcs-25/scoring.d.ts +32 -0
- package/dist/cjs/hcs-25/scoring.d.ts.map +1 -0
- package/dist/cjs/hcs-25/types.d.ts +363 -0
- package/dist/cjs/hcs-25/types.d.ts.map +1 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/standards-sdk.cjs +1 -1
- package/dist/cjs/standards-sdk.cjs.map +1 -1
- package/dist/es/hcs-25/identifiers.d.ts +24 -0
- package/dist/es/hcs-25/identifiers.d.ts.map +1 -0
- package/dist/es/hcs-25/index.d.ts +4 -0
- package/dist/es/hcs-25/index.d.ts.map +1 -0
- package/dist/es/hcs-25/scoring.d.ts +32 -0
- package/dist/es/hcs-25/scoring.d.ts.map +1 -0
- package/dist/es/hcs-25/types.d.ts +363 -0
- package/dist/es/hcs-25/types.d.ts.map +1 -0
- package/dist/es/index.d.ts +1 -0
- package/dist/es/index.d.ts.map +1 -1
- package/dist/es/standards-sdk.es.js +62 -40
- package/dist/es/standards-sdk.es.js.map +1 -1
- package/dist/es/standards-sdk.es101.js +2 -2
- package/dist/es/standards-sdk.es103.js +2 -2
- package/dist/es/standards-sdk.es104.js +1 -1
- package/dist/es/standards-sdk.es106.js +1 -1
- package/dist/es/standards-sdk.es108.js +2 -2
- package/dist/es/standards-sdk.es11.js +1 -1
- package/dist/es/standards-sdk.es110.js +1 -1
- package/dist/es/standards-sdk.es111.js +2 -2
- package/dist/es/standards-sdk.es115.js +3 -3
- package/dist/es/standards-sdk.es116.js +4 -4
- package/dist/es/standards-sdk.es117.js +19 -251
- package/dist/es/standards-sdk.es117.js.map +1 -1
- package/dist/es/standards-sdk.es118.js +60 -475
- package/dist/es/standards-sdk.es118.js.map +1 -1
- package/dist/es/standards-sdk.es119.js +217 -86
- package/dist/es/standards-sdk.es119.js.map +1 -1
- package/dist/es/standards-sdk.es12.js +1 -1
- package/dist/es/standards-sdk.es120.js +242 -150
- package/dist/es/standards-sdk.es120.js.map +1 -1
- package/dist/es/standards-sdk.es121.js +470 -26
- package/dist/es/standards-sdk.es121.js.map +1 -1
- package/dist/es/standards-sdk.es122.js +103 -10
- package/dist/es/standards-sdk.es122.js.map +1 -1
- package/dist/es/standards-sdk.es123.js +145 -167
- package/dist/es/standards-sdk.es123.js.map +1 -1
- package/dist/es/standards-sdk.es124.js +30 -25
- package/dist/es/standards-sdk.es124.js.map +1 -1
- package/dist/es/standards-sdk.es125.js +10 -20
- package/dist/es/standards-sdk.es125.js.map +1 -1
- package/dist/es/standards-sdk.es126.js +159 -137
- package/dist/es/standards-sdk.es126.js.map +1 -1
- package/dist/es/standards-sdk.es127.js +27 -199
- package/dist/es/standards-sdk.es127.js.map +1 -1
- package/dist/es/standards-sdk.es128.js +15 -783
- package/dist/es/standards-sdk.es128.js.map +1 -1
- package/dist/es/standards-sdk.es129.js +157 -10
- package/dist/es/standards-sdk.es129.js.map +1 -1
- package/dist/es/standards-sdk.es13.js +1 -1
- package/dist/es/standards-sdk.es130.js +198 -564
- package/dist/es/standards-sdk.es130.js.map +1 -1
- package/dist/es/standards-sdk.es131.js +703 -539
- package/dist/es/standards-sdk.es131.js.map +1 -1
- package/dist/es/standards-sdk.es132.js +12 -12
- package/dist/es/standards-sdk.es132.js.map +1 -1
- package/dist/es/standards-sdk.es133.js +567 -2
- package/dist/es/standards-sdk.es133.js.map +1 -1
- package/dist/es/standards-sdk.es134.js +610 -71
- package/dist/es/standards-sdk.es134.js.map +1 -1
- package/dist/es/standards-sdk.es135.js +12 -39
- package/dist/es/standards-sdk.es135.js.map +1 -1
- package/dist/es/standards-sdk.es136.js +2 -2
- package/dist/es/standards-sdk.es136.js.map +1 -1
- package/dist/es/standards-sdk.es137.js +42 -190
- package/dist/es/standards-sdk.es137.js.map +1 -1
- package/dist/es/standards-sdk.es138.js +39 -1139
- package/dist/es/standards-sdk.es138.js.map +1 -1
- package/dist/es/standards-sdk.es139.js +2 -306
- package/dist/es/standards-sdk.es139.js.map +1 -1
- package/dist/es/standards-sdk.es14.js +1 -1
- package/dist/es/standards-sdk.es140.js +231 -418
- package/dist/es/standards-sdk.es140.js.map +1 -1
- package/dist/es/standards-sdk.es141.js +1135 -350
- package/dist/es/standards-sdk.es141.js.map +1 -1
- package/dist/es/standards-sdk.es142.js +268 -1084
- package/dist/es/standards-sdk.es142.js.map +1 -1
- package/dist/es/standards-sdk.es143.js +418 -204
- package/dist/es/standards-sdk.es143.js.map +1 -1
- package/dist/es/standards-sdk.es144.js +350 -1562
- package/dist/es/standards-sdk.es144.js.map +1 -1
- package/dist/es/standards-sdk.es145.js +1061 -1096
- package/dist/es/standards-sdk.es145.js.map +1 -1
- package/dist/es/standards-sdk.es146.js +204 -13
- package/dist/es/standards-sdk.es146.js.map +1 -1
- package/dist/es/standards-sdk.es147.js +1541 -233
- package/dist/es/standards-sdk.es147.js.map +1 -1
- package/dist/es/standards-sdk.es148.js +1157 -4
- package/dist/es/standards-sdk.es148.js.map +1 -1
- package/dist/es/standards-sdk.es149.js +15 -79
- package/dist/es/standards-sdk.es149.js.map +1 -1
- package/dist/es/standards-sdk.es15.js +1 -1
- package/dist/es/standards-sdk.es150.js +250 -78
- package/dist/es/standards-sdk.es150.js.map +1 -1
- package/dist/es/standards-sdk.es151.js +4 -60
- package/dist/es/standards-sdk.es151.js.map +1 -1
- package/dist/es/standards-sdk.es152.js +80 -2546
- package/dist/es/standards-sdk.es152.js.map +1 -1
- package/dist/es/standards-sdk.es153.js +80 -152
- package/dist/es/standards-sdk.es153.js.map +1 -1
- package/dist/es/standards-sdk.es154.js +60 -7
- package/dist/es/standards-sdk.es154.js.map +1 -1
- package/dist/es/standards-sdk.es155.js +2547 -86
- package/dist/es/standards-sdk.es155.js.map +1 -1
- package/dist/es/standards-sdk.es156.js +144 -50
- package/dist/es/standards-sdk.es156.js.map +1 -1
- package/dist/es/standards-sdk.es157.js +7 -30
- package/dist/es/standards-sdk.es157.js.map +1 -1
- package/dist/es/standards-sdk.es158.js +86 -34
- package/dist/es/standards-sdk.es158.js.map +1 -1
- package/dist/es/standards-sdk.es159.js +65 -48
- package/dist/es/standards-sdk.es159.js.map +1 -1
- package/dist/es/standards-sdk.es16.js +5 -5
- package/dist/es/standards-sdk.es160.js +30 -138
- package/dist/es/standards-sdk.es160.js.map +1 -1
- package/dist/es/standards-sdk.es161.js +34 -42
- package/dist/es/standards-sdk.es161.js.map +1 -1
- package/dist/es/standards-sdk.es162.js +48 -12482
- package/dist/es/standards-sdk.es162.js.map +1 -1
- package/dist/es/standards-sdk.es163.js +12479 -53
- package/dist/es/standards-sdk.es163.js.map +1 -1
- package/dist/es/standards-sdk.es164.js +124 -97
- package/dist/es/standards-sdk.es164.js.map +1 -1
- package/dist/es/standards-sdk.es165.js +35 -74
- package/dist/es/standards-sdk.es165.js.map +1 -1
- package/dist/es/standards-sdk.es166.js +14 -197
- package/dist/es/standards-sdk.es166.js.map +1 -1
- package/dist/es/standards-sdk.es167.js +54 -15
- package/dist/es/standards-sdk.es167.js.map +1 -1
- package/dist/es/standards-sdk.es168.js +90 -651
- package/dist/es/standards-sdk.es168.js.map +1 -1
- package/dist/es/standards-sdk.es169.js +70 -161
- package/dist/es/standards-sdk.es169.js.map +1 -1
- package/dist/es/standards-sdk.es170.js +187 -309
- package/dist/es/standards-sdk.es170.js.map +1 -1
- package/dist/es/standards-sdk.es171.js +661 -335
- package/dist/es/standards-sdk.es171.js.map +1 -1
- package/dist/es/standards-sdk.es172.js +64 -444
- package/dist/es/standards-sdk.es172.js.map +1 -1
- package/dist/es/standards-sdk.es173.js +142 -301
- package/dist/es/standards-sdk.es173.js.map +1 -1
- package/dist/es/standards-sdk.es174.js +310 -64
- package/dist/es/standards-sdk.es174.js.map +1 -1
- package/dist/es/standards-sdk.es175.js +337 -64
- package/dist/es/standards-sdk.es175.js.map +1 -1
- package/dist/es/standards-sdk.es176.js +438 -124
- package/dist/es/standards-sdk.es176.js.map +1 -1
- package/dist/es/standards-sdk.es177.js +310 -496
- package/dist/es/standards-sdk.es177.js.map +1 -1
- package/dist/es/standards-sdk.es178.js +60 -243
- package/dist/es/standards-sdk.es178.js.map +1 -1
- package/dist/es/standards-sdk.es179.js +118 -175
- package/dist/es/standards-sdk.es179.js.map +1 -1
- package/dist/es/standards-sdk.es18.js +12 -12
- package/dist/es/standards-sdk.es180.js +465 -67
- package/dist/es/standards-sdk.es180.js.map +1 -1
- package/dist/es/standards-sdk.es181.js +250 -143
- package/dist/es/standards-sdk.es181.js.map +1 -1
- package/dist/es/standards-sdk.es182.js +172 -160
- package/dist/es/standards-sdk.es182.js.map +1 -1
- package/dist/es/standards-sdk.es183.js +65 -630
- package/dist/es/standards-sdk.es183.js.map +1 -1
- package/dist/es/standards-sdk.es184.js +128 -161
- package/dist/es/standards-sdk.es184.js.map +1 -1
- package/dist/es/standards-sdk.es185.js +144 -195
- package/dist/es/standards-sdk.es185.js.map +1 -1
- package/dist/es/standards-sdk.es186.js +668 -223
- package/dist/es/standards-sdk.es186.js.map +1 -1
- package/dist/es/standards-sdk.es187.js +188 -0
- package/dist/es/standards-sdk.es187.js.map +1 -0
- package/dist/es/standards-sdk.es188.js +238 -0
- package/dist/es/standards-sdk.es188.js.map +1 -0
- package/dist/es/standards-sdk.es189.js +242 -0
- package/dist/es/standards-sdk.es189.js.map +1 -0
- package/dist/es/standards-sdk.es19.js +9 -9
- package/dist/es/standards-sdk.es2.js +2 -2
- package/dist/es/standards-sdk.es20.js +1 -1
- package/dist/es/standards-sdk.es21.js +1 -1
- package/dist/es/standards-sdk.es22.js +1 -1
- package/dist/es/standards-sdk.es23.js +1 -1
- package/dist/es/standards-sdk.es24.js +1 -1
- package/dist/es/standards-sdk.es25.js +1 -1
- package/dist/es/standards-sdk.es26.js +1 -1
- package/dist/es/standards-sdk.es27.js +12 -12
- package/dist/es/standards-sdk.es30.js +2 -2
- package/dist/es/standards-sdk.es31.js +4 -4
- package/dist/es/standards-sdk.es32.js +1 -1
- package/dist/es/standards-sdk.es35.js +6 -6
- package/dist/es/standards-sdk.es36.js +4 -4
- package/dist/es/standards-sdk.es37.js +2 -2
- package/dist/es/standards-sdk.es38.js +2 -2
- package/dist/es/standards-sdk.es39.js +1 -1
- package/dist/es/standards-sdk.es4.js +2 -2
- package/dist/es/standards-sdk.es40.js +1 -1
- package/dist/es/standards-sdk.es41.js +2 -2
- package/dist/es/standards-sdk.es46.js +1 -1
- package/dist/es/standards-sdk.es5.js +2 -2
- package/dist/es/standards-sdk.es51.js +1 -1
- package/dist/es/standards-sdk.es53.js +1 -1
- package/dist/es/standards-sdk.es56.js +2 -2
- package/dist/es/standards-sdk.es59.js +1 -1
- package/dist/es/standards-sdk.es6.js +2 -2
- package/dist/es/standards-sdk.es60.js +1 -1
- package/dist/es/standards-sdk.es62.js +1 -1
- package/dist/es/standards-sdk.es63.js +2 -2
- package/dist/es/standards-sdk.es64.js +1 -1
- package/dist/es/standards-sdk.es65.js +1 -1
- package/dist/es/standards-sdk.es66.js +1 -1
- package/dist/es/standards-sdk.es67.js +7 -7
- package/dist/es/standards-sdk.es69.js +1 -1
- package/dist/es/standards-sdk.es7.js +1 -1
- package/dist/es/standards-sdk.es71.js +2 -2
- package/dist/es/standards-sdk.es72.js +3 -3
- package/dist/es/standards-sdk.es75.js +5 -5
- package/dist/es/standards-sdk.es76.js +3 -3
- package/dist/es/standards-sdk.es77.js +2 -2
- package/dist/es/standards-sdk.es78.js +1 -1
- package/dist/es/standards-sdk.es81.js +2 -2
- package/dist/es/standards-sdk.es83.js +2 -2
- package/dist/es/standards-sdk.es84.js +4 -4
- package/dist/es/standards-sdk.es85.js +1 -1
- package/dist/es/standards-sdk.es88.js +1 -1
- package/dist/es/standards-sdk.es89.js +2 -2
- package/dist/es/standards-sdk.es9.js +2 -2
- package/dist/es/standards-sdk.es90.js +4 -4
- package/dist/es/standards-sdk.es94.js +3 -3
- package/dist/es/standards-sdk.es96.js +2 -2
- package/dist/es/standards-sdk.es98.js +1 -1
- package/dist/es/standards-sdk.es99.js +3 -3
- package/package.json +1 -1
|
@@ -1,629 +1,793 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
1
|
+
import { proto } from "@hashgraph/proto";
|
|
2
|
+
import { Buffer } from "buffer";
|
|
3
|
+
import { Hbar, Long, HbarUnit, Transaction } from "@hashgraph/sdk";
|
|
4
|
+
import { ethers } from "ethers";
|
|
5
|
+
import { TransactionParsingError } from "./standards-sdk.es132.js";
|
|
6
|
+
import { resolveTransactionSummary } from "./standards-sdk.es133.js";
|
|
7
|
+
import { HTSParser } from "./standards-sdk.es141.js";
|
|
8
|
+
import { HCSParser } from "./standards-sdk.es173.js";
|
|
9
|
+
import { FileParser } from "./standards-sdk.es174.js";
|
|
10
|
+
import { CryptoParser } from "./standards-sdk.es175.js";
|
|
11
|
+
import { SCSParser } from "./standards-sdk.es176.js";
|
|
12
|
+
import { UtilParser } from "./standards-sdk.es177.js";
|
|
13
|
+
import { ScheduleParser } from "./standards-sdk.es142.js";
|
|
14
|
+
import { transactionParserRegistry } from "./standards-sdk.es143.js";
|
|
15
|
+
import { getTransactionTypeFromBody, getHumanReadableTransactionType } from "./standards-sdk.es144.js";
|
|
16
|
+
class TransactionParser {
|
|
9
17
|
/**
|
|
10
|
-
*
|
|
18
|
+
* Parse transaction bytes in any supported format (base64 or hex, regular or scheduled)
|
|
19
|
+
* This is the main entry point for transaction parsing with enhanced retry logic
|
|
11
20
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* For ambiguous cases (where confidence is 'uncertain'), if you have the associated account ID,
|
|
16
|
-
* you can use the Hedera mirror node to confirm the key type.
|
|
17
|
-
*
|
|
18
|
-
* @param keyInput - The key to detect, can be a string (hex, base64, PEM), Buffer, or Uint8Array
|
|
19
|
-
* @returns KeyInfo object containing the detected key type and metadata
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* // Detect from hex string
|
|
23
|
-
* const info1 = KeyTypeDetector.detect('0x7f96cea0c0d9d5bfb3ab8a42cf0cea44d57f62f2e068f8e5a3251914a9252b04');
|
|
24
|
-
*
|
|
25
|
-
* // Detect from DER-encoded string
|
|
26
|
-
* const info2 = KeyTypeDetector.detect(privateKey.toStringDer());
|
|
27
|
-
*
|
|
28
|
-
* // Detect from PEM format
|
|
29
|
-
* const info3 = KeyTypeDetector.detect(`-----BEGIN PRIVATE KEY-----
|
|
30
|
-
* MC4CAQAwBQYDK2VwBCIEIGRchBsQGQduAAGBQ7GAkKCKkmQ3EGCARwHsHiRqTNR9
|
|
31
|
-
* -----END PRIVATE KEY-----`);
|
|
21
|
+
* @param transactionBytes - Transaction bytes in base64 or hex format
|
|
22
|
+
* @param options - Parsing options and configuration
|
|
23
|
+
* @returns Promise resolving to ParsedTransaction
|
|
32
24
|
*/
|
|
33
|
-
static
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
25
|
+
static async parseTransactionBytes(transactionBytes, options = {}) {
|
|
26
|
+
const {
|
|
27
|
+
enableFallback = true,
|
|
28
|
+
strictMode = false,
|
|
29
|
+
includeRaw = true,
|
|
30
|
+
maxRetries = 2
|
|
31
|
+
} = options;
|
|
32
|
+
const validation = this.validateTransactionBytes(transactionBytes);
|
|
33
|
+
if (!validation.isValid && strictMode) {
|
|
34
|
+
throw new TransactionParsingError(
|
|
35
|
+
`Invalid transaction bytes format: ${validation.error}`,
|
|
36
|
+
"INVALID_FORMAT",
|
|
37
|
+
void 0,
|
|
38
|
+
transactionBytes
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
let lastError;
|
|
42
|
+
let retryCount = 0;
|
|
43
|
+
while (retryCount <= maxRetries) {
|
|
44
|
+
try {
|
|
45
|
+
const result = await this.parseTransaction(transactionBytes, options);
|
|
46
|
+
result.formatDetection = {
|
|
47
|
+
originalFormat: validation.format || "base64",
|
|
48
|
+
wasConverted: validation.format === "hex",
|
|
49
|
+
length: transactionBytes.length
|
|
50
|
+
};
|
|
51
|
+
return result;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
54
|
+
retryCount++;
|
|
55
|
+
if (!enableFallback || retryCount > maxRetries) {
|
|
56
|
+
if (strictMode) {
|
|
57
|
+
throw new TransactionParsingError(
|
|
58
|
+
"Failed to parse transaction after all attempts",
|
|
59
|
+
"PARSING_FAILED",
|
|
60
|
+
lastError,
|
|
61
|
+
transactionBytes
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
44
65
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
format: "raw",
|
|
55
|
-
isPrivateKey: false,
|
|
56
|
-
confidence: "certain"
|
|
66
|
+
try {
|
|
67
|
+
const result = this.parseScheduledTransaction(
|
|
68
|
+
transactionBytes,
|
|
69
|
+
options
|
|
70
|
+
);
|
|
71
|
+
result.formatDetection = {
|
|
72
|
+
originalFormat: validation.format || "base64",
|
|
73
|
+
wasConverted: false,
|
|
74
|
+
length: transactionBytes.length
|
|
57
75
|
};
|
|
76
|
+
return result;
|
|
77
|
+
} catch (scheduledError) {
|
|
58
78
|
}
|
|
59
|
-
} else if (keyInput instanceof Buffer) {
|
|
60
|
-
keyBytes = new Uint8Array(keyInput);
|
|
61
|
-
} else {
|
|
62
|
-
keyBytes = keyInput;
|
|
63
79
|
}
|
|
64
|
-
return this.detectFromBytes(keyBytes, originalFormat);
|
|
65
|
-
} catch (error) {
|
|
66
|
-
return {
|
|
67
|
-
type: "unknown",
|
|
68
|
-
format: "raw",
|
|
69
|
-
isPrivateKey: false,
|
|
70
|
-
confidence: "certain"
|
|
71
|
-
};
|
|
72
80
|
}
|
|
81
|
+
const fallbackResult = this.createFallbackResult(
|
|
82
|
+
transactionBytes,
|
|
83
|
+
lastError,
|
|
84
|
+
void 0
|
|
85
|
+
);
|
|
86
|
+
if (fallbackResult.details) {
|
|
87
|
+
fallbackResult.details.parseAttempts = Math.max(retryCount, 1);
|
|
88
|
+
}
|
|
89
|
+
return fallbackResult;
|
|
73
90
|
}
|
|
74
91
|
/**
|
|
75
|
-
*
|
|
92
|
+
* Parse a Transaction object directly using unified delegation approach
|
|
93
|
+
* This method delegates to specialized parsers for clean separation of concerns
|
|
76
94
|
*
|
|
77
|
-
* @param
|
|
78
|
-
* @param
|
|
79
|
-
* @
|
|
80
|
-
* @
|
|
95
|
+
* @param transaction - The Transaction object to parse
|
|
96
|
+
* @param originalBytes - The original transaction bytes (optional, for protobuf parsing)
|
|
97
|
+
* @param options - Parsing options
|
|
98
|
+
* @returns Parsed transaction data
|
|
81
99
|
*/
|
|
82
|
-
static
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
};
|
|
100
|
+
static parseTransactionObject(transaction, originalBytesOrOptions, options = {}) {
|
|
101
|
+
let originalBytes;
|
|
102
|
+
let actualOptions;
|
|
103
|
+
if (originalBytesOrOptions && !Buffer.isBuffer(originalBytesOrOptions) && !(originalBytesOrOptions instanceof Uint8Array)) {
|
|
104
|
+
actualOptions = originalBytesOrOptions;
|
|
105
|
+
originalBytes = void 0;
|
|
106
|
+
} else {
|
|
107
|
+
originalBytes = originalBytesOrOptions;
|
|
108
|
+
actualOptions = options;
|
|
92
109
|
}
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
try {
|
|
111
|
+
const metadata = this.extractTransactionMetadata(transaction);
|
|
112
|
+
const htsResult = HTSParser.parseFromTransactionObject(transaction);
|
|
113
|
+
const cryptoResult = CryptoParser.parseFromTransactionObject(transaction);
|
|
114
|
+
const hcsResult = HCSParser.parseFromTransactionObject(transaction);
|
|
115
|
+
const fileResult = FileParser.parseFromTransactionObject(transaction);
|
|
116
|
+
const scsResult = SCSParser.parseFromTransactionObject(transaction);
|
|
117
|
+
const scheduleResult = ScheduleParser.parseFromTransactionObject(transaction);
|
|
118
|
+
const utilResult = UtilParser.parseFromTransactionObject(transaction);
|
|
119
|
+
const results = [
|
|
120
|
+
htsResult,
|
|
121
|
+
cryptoResult,
|
|
122
|
+
hcsResult,
|
|
123
|
+
fileResult,
|
|
124
|
+
scsResult,
|
|
125
|
+
scheduleResult,
|
|
126
|
+
utilResult
|
|
127
|
+
];
|
|
128
|
+
const primaryResult = results.find((result2) => result2.type && result2.type !== "UNKNOWN") || {};
|
|
129
|
+
let finalType = "UNKNOWN";
|
|
130
|
+
let finalHumanReadableType = "Unknown Transaction";
|
|
131
|
+
let parsedTokenCreation;
|
|
132
|
+
let protoParsingResult = {};
|
|
133
|
+
if (originalBytes || transaction.toBytes) {
|
|
134
|
+
try {
|
|
135
|
+
const bytesToParse = originalBytes || transaction.toBytes();
|
|
136
|
+
const decoded = proto.TransactionList.decode(bytesToParse);
|
|
137
|
+
if (decoded.transactionList && decoded.transactionList.length > 0) {
|
|
138
|
+
const tx = decoded.transactionList[0];
|
|
139
|
+
if (tx.bodyBytes && tx.bodyBytes.length > 0) {
|
|
140
|
+
const txBody = proto.TransactionBody.decode(tx.bodyBytes);
|
|
141
|
+
const typeResult = this.detectTransactionTypeFromBody(txBody);
|
|
142
|
+
finalType = typeResult.type;
|
|
143
|
+
finalHumanReadableType = typeResult.humanReadableType;
|
|
144
|
+
protoParsingResult = this.parseTransactionBodyDetails(
|
|
145
|
+
txBody,
|
|
146
|
+
finalType
|
|
147
|
+
);
|
|
148
|
+
if (protoParsingResult.tokenCreation) {
|
|
149
|
+
parsedTokenCreation = protoParsingResult.tokenCreation;
|
|
150
|
+
}
|
|
151
|
+
} else if (tx.signedTransactionBytes && tx.signedTransactionBytes.length > 0) {
|
|
152
|
+
const signedTx = proto.SignedTransaction.decode(
|
|
153
|
+
tx.signedTransactionBytes
|
|
154
|
+
);
|
|
155
|
+
if (signedTx.bodyBytes) {
|
|
156
|
+
const txBody = proto.TransactionBody.decode(signedTx.bodyBytes);
|
|
157
|
+
const typeResult = this.detectTransactionTypeFromBody(txBody);
|
|
158
|
+
finalType = typeResult.type;
|
|
159
|
+
finalHumanReadableType = typeResult.humanReadableType;
|
|
160
|
+
protoParsingResult = this.parseTransactionBodyDetails(
|
|
161
|
+
txBody,
|
|
162
|
+
finalType
|
|
163
|
+
);
|
|
164
|
+
if (protoParsingResult.tokenCreation) {
|
|
165
|
+
parsedTokenCreation = protoParsingResult.tokenCreation;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} catch (protoError) {
|
|
171
|
+
}
|
|
103
172
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const keyStart = this.ECDSA_SECP256K1_PRIVATE_KEY_PREFIX_SHORT.length;
|
|
117
|
-
return {
|
|
118
|
-
type: "ecdsa",
|
|
119
|
-
format: "der",
|
|
120
|
-
isPrivateKey: true,
|
|
121
|
-
rawBytes: bytes.slice(keyStart),
|
|
122
|
-
confidence: "certain"
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
if (this.hasPrefix(bytes, this.ECDSA_SECP256K1_PRIVATE_KEY_PREFIX)) {
|
|
126
|
-
const keyStart = this.ECDSA_SECP256K1_PRIVATE_KEY_PREFIX.length;
|
|
127
|
-
return {
|
|
128
|
-
type: "ecdsa",
|
|
129
|
-
format: "der",
|
|
130
|
-
isPrivateKey: true,
|
|
131
|
-
rawBytes: bytes.slice(keyStart, keyStart + 32),
|
|
132
|
-
confidence: "certain"
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
if (this.hasPrefix(bytes, this.ECDSA_SECP256K1_PRIVATE_KEY_PREFIX_LONG)) {
|
|
136
|
-
const keyStart = this.ECDSA_SECP256K1_PRIVATE_KEY_PREFIX_LONG.length;
|
|
137
|
-
return {
|
|
138
|
-
type: "ecdsa",
|
|
139
|
-
format: "der",
|
|
140
|
-
isPrivateKey: true,
|
|
141
|
-
rawBytes: bytes.slice(keyStart, keyStart + 32),
|
|
142
|
-
confidence: "certain"
|
|
173
|
+
if (finalType === "UNKNOWN" && primaryResult.type) {
|
|
174
|
+
finalType = primaryResult.type;
|
|
175
|
+
finalHumanReadableType = primaryResult.humanReadableType || finalHumanReadableType;
|
|
176
|
+
}
|
|
177
|
+
const result = {
|
|
178
|
+
type: finalType,
|
|
179
|
+
humanReadableType: finalHumanReadableType,
|
|
180
|
+
transfers: [],
|
|
181
|
+
tokenTransfers: [],
|
|
182
|
+
...metadata,
|
|
183
|
+
...primaryResult,
|
|
184
|
+
raw: actualOptions.includeRaw ? transaction._transactionBody || {} : void 0
|
|
143
185
|
};
|
|
144
|
-
|
|
145
|
-
|
|
186
|
+
this.mergeProtoParsingResults(
|
|
187
|
+
result,
|
|
188
|
+
protoParsingResult,
|
|
189
|
+
htsResult,
|
|
190
|
+
transaction,
|
|
191
|
+
originalBytes
|
|
192
|
+
);
|
|
193
|
+
result.transfers = cryptoResult.transfers || result.transfers || [];
|
|
194
|
+
result.tokenTransfers = cryptoResult.tokenTransfers || result.tokenTransfers || [];
|
|
195
|
+
return result;
|
|
196
|
+
} catch (error) {
|
|
146
197
|
return {
|
|
147
|
-
type: "
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
switch (bytes.length) {
|
|
155
|
-
case this.ED25519_PUBLIC_KEY_LENGTH:
|
|
156
|
-
if (format === "hex") {
|
|
157
|
-
const hexStr = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
158
|
-
const privateResult = this.tryCreateKey(hexStr);
|
|
159
|
-
if (privateResult.type !== "unknown") {
|
|
160
|
-
return privateResult;
|
|
161
|
-
}
|
|
198
|
+
type: "UNKNOWN",
|
|
199
|
+
humanReadableType: "Unknown Transaction",
|
|
200
|
+
transfers: [],
|
|
201
|
+
tokenTransfers: [],
|
|
202
|
+
raw: actualOptions.includeRaw ? {} : void 0,
|
|
203
|
+
details: {
|
|
204
|
+
error: `Failed to parse Transaction object: ${error instanceof Error ? error.message : String(error)}`
|
|
162
205
|
}
|
|
163
|
-
|
|
164
|
-
type: "unknown",
|
|
165
|
-
format,
|
|
166
|
-
isPrivateKey: false,
|
|
167
|
-
rawBytes: bytes,
|
|
168
|
-
confidence: "certain"
|
|
169
|
-
};
|
|
170
|
-
case this.ED25519_EXPANDED_PRIVATE_KEY_LENGTH:
|
|
171
|
-
return {
|
|
172
|
-
type: "ed25519",
|
|
173
|
-
format,
|
|
174
|
-
isPrivateKey: true,
|
|
175
|
-
rawBytes: bytes,
|
|
176
|
-
confidence: "certain"
|
|
177
|
-
};
|
|
206
|
+
};
|
|
178
207
|
}
|
|
179
|
-
return {
|
|
180
|
-
type: "unknown",
|
|
181
|
-
format,
|
|
182
|
-
isPrivateKey: false,
|
|
183
|
-
rawBytes: bytes,
|
|
184
|
-
confidence: "certain"
|
|
185
|
-
};
|
|
186
208
|
}
|
|
187
209
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* @
|
|
191
|
-
* @returns KeyInfo object with detection results
|
|
192
|
-
* @private
|
|
210
|
+
* Parse a base64 encoded transaction body using registry pattern
|
|
211
|
+
* @param transactionBodyBase64 - The base64 encoded transaction body
|
|
212
|
+
* @returns The parsed transaction
|
|
193
213
|
*/
|
|
194
|
-
static
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
214
|
+
static parseTransactionBody(transactionBodyBase64) {
|
|
215
|
+
try {
|
|
216
|
+
const buffer = ethers.decodeBase64(transactionBodyBase64);
|
|
217
|
+
const txBody = proto.SchedulableTransactionBody.decode(buffer);
|
|
218
|
+
const transactionType = this.getTransactionType(txBody);
|
|
219
|
+
const result = {
|
|
220
|
+
type: transactionType,
|
|
221
|
+
humanReadableType: this.getHumanReadableType(transactionType),
|
|
222
|
+
transfers: [],
|
|
223
|
+
tokenTransfers: [],
|
|
224
|
+
raw: txBody
|
|
225
|
+
};
|
|
226
|
+
if (txBody.memo) {
|
|
227
|
+
result.memo = txBody.memo;
|
|
208
228
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
229
|
+
if (txBody.transactionFee) {
|
|
230
|
+
const hbarAmount = Hbar.fromTinybars(
|
|
231
|
+
Long.fromValue(txBody.transactionFee)
|
|
232
|
+
);
|
|
233
|
+
result.transactionFee = hbarAmount.toString(HbarUnit.Hbar);
|
|
234
|
+
}
|
|
235
|
+
this.applySchedulableTransactionParsing(txBody, result);
|
|
236
|
+
return result;
|
|
237
|
+
} catch (error) {
|
|
212
238
|
return {
|
|
213
|
-
type: "
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
239
|
+
type: "UNKNOWN",
|
|
240
|
+
humanReadableType: "Unknown Transaction",
|
|
241
|
+
transfers: [],
|
|
242
|
+
tokenTransfers: [],
|
|
243
|
+
raw: void 0,
|
|
244
|
+
details: {
|
|
245
|
+
error: `Failed to parse transaction body: ${error instanceof Error ? error.message : String(error)}`
|
|
246
|
+
}
|
|
217
247
|
};
|
|
218
248
|
}
|
|
219
|
-
|
|
220
|
-
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Detect transaction type and human-readable type from transaction body protobuf
|
|
252
|
+
* Uses registry pattern to eliminate massive if-else chain
|
|
253
|
+
*/
|
|
254
|
+
static detectTransactionTypeFromBody(txBody) {
|
|
255
|
+
return getTransactionTypeFromBody(txBody);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Parse details from a complete schedule response
|
|
259
|
+
* @param scheduleResponse - The schedule response to parse
|
|
260
|
+
* @returns The parsed transaction
|
|
261
|
+
*/
|
|
262
|
+
static parseScheduleResponse(scheduleResponse) {
|
|
263
|
+
if (!scheduleResponse.transaction_body) {
|
|
221
264
|
return {
|
|
222
|
-
type: "
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
265
|
+
type: "UNKNOWN",
|
|
266
|
+
humanReadableType: "Unknown Transaction",
|
|
267
|
+
transfers: [],
|
|
268
|
+
tokenTransfers: [],
|
|
269
|
+
raw: void 0,
|
|
270
|
+
details: {
|
|
271
|
+
error: "Schedule response missing transaction_body"
|
|
272
|
+
}
|
|
226
273
|
};
|
|
227
274
|
}
|
|
228
|
-
const
|
|
229
|
-
if (
|
|
230
|
-
|
|
231
|
-
type: "unknown",
|
|
232
|
-
format: "pem",
|
|
233
|
-
isPrivateKey,
|
|
234
|
-
confidence: "certain"
|
|
235
|
-
};
|
|
275
|
+
const parsed = this.parseTransactionBody(scheduleResponse.transaction_body);
|
|
276
|
+
if (scheduleResponse.memo) {
|
|
277
|
+
parsed.memo = scheduleResponse.memo;
|
|
236
278
|
}
|
|
237
|
-
|
|
238
|
-
|
|
279
|
+
return parsed;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Determine the transaction type using registry pattern
|
|
283
|
+
* @param txBody - The transaction body to determine the type of
|
|
284
|
+
* @returns The type of the transaction
|
|
285
|
+
*/
|
|
286
|
+
static getTransactionType(txBody) {
|
|
287
|
+
return getTransactionTypeFromBody(txBody).type;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Convert technical transaction type to human-readable format using registry pattern
|
|
291
|
+
* @param type - The technical transaction type
|
|
292
|
+
* @returns The human-readable transaction type
|
|
293
|
+
*/
|
|
294
|
+
static getHumanReadableType(type) {
|
|
295
|
+
return getHumanReadableTransactionType(type);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Get a human-readable summary of the transaction
|
|
299
|
+
* @param parsedTx - The parsed transaction
|
|
300
|
+
* @returns The human-readable summary of the transaction
|
|
301
|
+
*/
|
|
302
|
+
static getTransactionSummary(parsedTx) {
|
|
303
|
+
return resolveTransactionSummary(parsedTx);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Validate transaction bytes format and encoding
|
|
307
|
+
* Enhanced validation with better format detection
|
|
308
|
+
*/
|
|
309
|
+
static validateTransactionBytes(transactionBytes) {
|
|
310
|
+
if (!transactionBytes || typeof transactionBytes !== "string") {
|
|
239
311
|
return {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
isPrivateKey,
|
|
243
|
-
confidence: "certain"
|
|
312
|
+
isValid: false,
|
|
313
|
+
error: "Transaction bytes must be a non-empty string"
|
|
244
314
|
};
|
|
245
315
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
316
|
+
const format = this.detectTransactionFormat(transactionBytes);
|
|
317
|
+
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
318
|
+
const hexRegex = /^0x[0-9a-fA-F]+$/;
|
|
319
|
+
let isValid = false;
|
|
320
|
+
let error;
|
|
321
|
+
if (format === "hex") {
|
|
322
|
+
isValid = hexRegex.test(transactionBytes) && transactionBytes.length > 2;
|
|
323
|
+
if (!isValid) {
|
|
324
|
+
error = "Invalid hex format";
|
|
325
|
+
}
|
|
326
|
+
} else {
|
|
327
|
+
isValid = base64Regex.test(transactionBytes) && transactionBytes.length > 0;
|
|
328
|
+
if (!isValid) {
|
|
329
|
+
error = "Invalid base64 format";
|
|
257
330
|
}
|
|
258
|
-
return {
|
|
259
|
-
...result,
|
|
260
|
-
format: "pem"
|
|
261
|
-
};
|
|
262
|
-
} catch {
|
|
263
|
-
return {
|
|
264
|
-
type: "unknown",
|
|
265
|
-
format: "pem",
|
|
266
|
-
isPrivateKey,
|
|
267
|
-
confidence: "certain"
|
|
268
|
-
};
|
|
269
331
|
}
|
|
332
|
+
return {
|
|
333
|
+
isValid,
|
|
334
|
+
format,
|
|
335
|
+
error,
|
|
336
|
+
length: transactionBytes.length
|
|
337
|
+
};
|
|
270
338
|
}
|
|
271
339
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* @
|
|
275
|
-
|
|
276
|
-
|
|
340
|
+
* Detects the format of transaction bytes
|
|
341
|
+
* @param transactionBytes - The transaction bytes to analyze
|
|
342
|
+
* @returns The detected format ('base64' or 'hex')
|
|
343
|
+
*/
|
|
344
|
+
static detectTransactionFormat(transactionBytes) {
|
|
345
|
+
return transactionBytes.startsWith("0x") ? "hex" : "base64";
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Decodes transaction bytes from string to Uint8Array
|
|
349
|
+
* @param transactionBytes - The transaction bytes string (base64 or hex)
|
|
350
|
+
* @returns Decoded Uint8Array
|
|
351
|
+
* @throws TransactionParsingError if decoding fails
|
|
277
352
|
*/
|
|
278
|
-
static
|
|
353
|
+
static decodeTransactionBytes(transactionBytes) {
|
|
279
354
|
try {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const keyBytes = this.hexToBytes(keyStr);
|
|
287
|
-
const signatureResult = this.detectBySignature(keyStr);
|
|
288
|
-
if (signatureResult.type !== "unknown") {
|
|
289
|
-
return {
|
|
290
|
-
type: signatureResult.type,
|
|
291
|
-
format: "hex",
|
|
292
|
-
isPrivateKey: true,
|
|
293
|
-
rawBytes: keyBytes,
|
|
294
|
-
confidence: signatureResult.confidence,
|
|
295
|
-
warning: signatureResult.warning
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
return {
|
|
299
|
-
type: "unknown",
|
|
300
|
-
format: "hex",
|
|
301
|
-
isPrivateKey: false,
|
|
302
|
-
rawBytes: keyBytes,
|
|
303
|
-
confidence: "certain"
|
|
304
|
-
};
|
|
305
|
-
}
|
|
355
|
+
const format = this.detectTransactionFormat(transactionBytes);
|
|
356
|
+
if (format === "hex") {
|
|
357
|
+
const hexString = transactionBytes.slice(2);
|
|
358
|
+
return new Uint8Array(Buffer.from(hexString, "hex"));
|
|
359
|
+
} else {
|
|
360
|
+
return new Uint8Array(Buffer.from(transactionBytes, "base64"));
|
|
306
361
|
}
|
|
307
|
-
} catch {
|
|
362
|
+
} catch (error) {
|
|
363
|
+
throw new TransactionParsingError(
|
|
364
|
+
"Failed to decode transaction bytes",
|
|
365
|
+
"DECODE_ERROR",
|
|
366
|
+
error instanceof Error ? error : void 0,
|
|
367
|
+
transactionBytes
|
|
368
|
+
);
|
|
308
369
|
}
|
|
309
|
-
return {
|
|
310
|
-
type: "unknown",
|
|
311
|
-
format: "hex",
|
|
312
|
-
isPrivateKey: false,
|
|
313
|
-
rawBytes: new Uint8Array(),
|
|
314
|
-
confidence: "certain"
|
|
315
|
-
};
|
|
316
370
|
}
|
|
317
371
|
/**
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* For ambiguous cases where both algorithms can use the key, this method
|
|
321
|
-
* uses heuristics to make a best-effort determination. For production use
|
|
322
|
-
* with ambiguous keys, consider using the Hedera mirror node to confirm
|
|
323
|
-
* the key type if you have the associated account ID.
|
|
324
|
-
*
|
|
325
|
-
* @param hexKey - The hex string representation of the key
|
|
326
|
-
* @returns Object containing type, confidence level, and optional warning
|
|
327
|
-
* @private
|
|
372
|
+
* Check if transaction bytes represent a valid Hedera transaction
|
|
328
373
|
*/
|
|
329
|
-
static
|
|
330
|
-
const keyBigInt = BigInt("0x" + hexKey);
|
|
331
|
-
const secp256k1Order = BigInt(
|
|
332
|
-
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
|
|
333
|
-
);
|
|
334
|
-
if (keyBigInt === 0n) {
|
|
335
|
-
return {
|
|
336
|
-
type: "ed25519",
|
|
337
|
-
confidence: "certain"
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
if (keyBigInt >= secp256k1Order) {
|
|
341
|
-
return {
|
|
342
|
-
type: "ed25519",
|
|
343
|
-
confidence: "certain"
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
let ed25519CanSign = false;
|
|
347
|
-
let ecdsaCanSign = false;
|
|
374
|
+
static async isValidHederaTransaction(transactionBytes) {
|
|
348
375
|
try {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const signature = ed25519Key.sign(testMessage);
|
|
352
|
-
if (ed25519Key.publicKey.verify(testMessage, signature)) {
|
|
353
|
-
ed25519CanSign = true;
|
|
376
|
+
if (!this.validateTransactionBytes(transactionBytes).isValid) {
|
|
377
|
+
return false;
|
|
354
378
|
}
|
|
379
|
+
const bytes = this.decodeTransactionBytes(transactionBytes);
|
|
380
|
+
Transaction.fromBytes(bytes);
|
|
381
|
+
return true;
|
|
355
382
|
} catch {
|
|
383
|
+
return false;
|
|
356
384
|
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Legacy alias for decodeTransactionBytes - keeping for backward compatibility
|
|
388
|
+
*/
|
|
389
|
+
static decodeBytesLegacy(transactionBytes) {
|
|
390
|
+
return this.decodeTransactionBytes(transactionBytes);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Parse transaction bytes into structured data using thin orchestration
|
|
394
|
+
* Delegates to specialized parsers for clean separation of concerns
|
|
395
|
+
*/
|
|
396
|
+
static async parseTransaction(transactionBytes, options = {}) {
|
|
357
397
|
try {
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
return {
|
|
382
|
-
type: "ecdsa",
|
|
383
|
-
confidence: "uncertain",
|
|
384
|
-
warning: "Detection based on entropy heuristic. Both ED25519 and ECDSA accept this key."
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
return {
|
|
388
|
-
type: "ecdsa",
|
|
389
|
-
confidence: "uncertain",
|
|
390
|
-
warning: "Detection based on entropy heuristic. Both ED25519 and ECDSA accept this key. Defaulting to ECDSA."
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
if (ed25519CanSign && !ecdsaCanSign) {
|
|
394
|
-
return {
|
|
395
|
-
type: "ed25519",
|
|
396
|
-
confidence: "certain"
|
|
398
|
+
const bytes = this.decodeTransactionBytes(transactionBytes);
|
|
399
|
+
const transaction = Transaction.fromBytes(bytes);
|
|
400
|
+
const metadata = this.extractTransactionMetadataEnhanced(transaction);
|
|
401
|
+
const parserResults = this.runAllParsers(transaction);
|
|
402
|
+
const primaryResult = parserResults.find(
|
|
403
|
+
(result2) => result2.type && result2.type !== "UNKNOWN"
|
|
404
|
+
) || {};
|
|
405
|
+
const protoParsingResult = this.parseFromProtobuf(bytes);
|
|
406
|
+
const finalType = protoParsingResult.type || primaryResult.type || "UNKNOWN";
|
|
407
|
+
const finalHumanReadableType = protoParsingResult.humanReadableType || primaryResult.humanReadableType || "Unknown Transaction";
|
|
408
|
+
const result = {
|
|
409
|
+
type: finalType,
|
|
410
|
+
humanReadableType: finalHumanReadableType,
|
|
411
|
+
transfers: [],
|
|
412
|
+
tokenTransfers: [],
|
|
413
|
+
details: { ...metadata },
|
|
414
|
+
memo: typeof metadata.memo === "string" ? metadata.memo : void 0,
|
|
415
|
+
transactionId: typeof metadata.transactionId === "string" ? metadata.transactionId : void 0,
|
|
416
|
+
nodeAccountIds: Array.isArray(metadata.nodeAccountIds) ? metadata.nodeAccountIds : [],
|
|
417
|
+
maxTransactionFee: typeof metadata.maxTransactionFee === "string" ? metadata.maxTransactionFee : void 0,
|
|
418
|
+
validStart: typeof metadata.validStart === "string" ? metadata.validStart : void 0,
|
|
419
|
+
validDuration: typeof metadata.validDuration === "string" ? metadata.validDuration : void 0,
|
|
420
|
+
raw: options.includeRaw ? {} : void 0
|
|
397
421
|
};
|
|
422
|
+
this.mergeAllResults(
|
|
423
|
+
result,
|
|
424
|
+
protoParsingResult,
|
|
425
|
+
primaryResult,
|
|
426
|
+
parserResults
|
|
427
|
+
);
|
|
428
|
+
return result;
|
|
429
|
+
} catch (error) {
|
|
430
|
+
throw new TransactionParsingError(
|
|
431
|
+
"Failed to parse as regular transaction",
|
|
432
|
+
"REGULAR_PARSING_FAILED",
|
|
433
|
+
error instanceof Error ? error : void 0,
|
|
434
|
+
transactionBytes
|
|
435
|
+
);
|
|
398
436
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Parse scheduled transaction body
|
|
440
|
+
*/
|
|
441
|
+
static parseScheduledTransaction(transactionBytes, options = {}) {
|
|
442
|
+
try {
|
|
443
|
+
const parsedTx = this.parseTransactionBody(transactionBytes);
|
|
444
|
+
const validation = this.validateTransactionBytes(transactionBytes);
|
|
445
|
+
const enhancedResult = {
|
|
446
|
+
...parsedTx,
|
|
447
|
+
details: {
|
|
448
|
+
...parsedTx.details || {},
|
|
449
|
+
parseMethod: "scheduled",
|
|
450
|
+
validation,
|
|
451
|
+
parseAttempts: parsedTx.details?.parseAttempts || 1
|
|
452
|
+
},
|
|
453
|
+
formatDetection: {
|
|
454
|
+
originalFormat: validation.format || "base64",
|
|
455
|
+
wasConverted: false,
|
|
456
|
+
length: transactionBytes.length
|
|
457
|
+
}
|
|
403
458
|
};
|
|
459
|
+
return enhancedResult;
|
|
460
|
+
} catch (error) {
|
|
461
|
+
throw new TransactionParsingError(
|
|
462
|
+
"Failed to parse as scheduled transaction",
|
|
463
|
+
"SCHEDULED_PARSING_FAILED",
|
|
464
|
+
error instanceof Error ? error : void 0,
|
|
465
|
+
transactionBytes
|
|
466
|
+
);
|
|
404
467
|
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Extract metadata from Transaction object (legacy method - keeping for backward compatibility)
|
|
471
|
+
*/
|
|
472
|
+
static extractTransactionMetadata(transaction) {
|
|
405
473
|
return {
|
|
406
|
-
|
|
407
|
-
|
|
474
|
+
transactionId: transaction.transactionId?.toString(),
|
|
475
|
+
nodeAccountIds: transaction.nodeAccountIds?.map((id) => id.toString()) || [],
|
|
476
|
+
maxTransactionFee: transaction.maxTransactionFee?.toTinybars().toString() || "0",
|
|
477
|
+
memo: transaction._transactionMemo || void 0,
|
|
478
|
+
transfers: [],
|
|
479
|
+
tokenTransfers: []
|
|
408
480
|
};
|
|
409
481
|
}
|
|
410
482
|
/**
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
* @param str - The string to check
|
|
414
|
-
* @returns True if the string is valid hex with even length
|
|
415
|
-
* @private
|
|
483
|
+
* Extract enhanced metadata from Transaction object
|
|
416
484
|
*/
|
|
417
|
-
static
|
|
418
|
-
return
|
|
485
|
+
static extractTransactionMetadataEnhanced(transaction) {
|
|
486
|
+
return {
|
|
487
|
+
transactionId: transaction.transactionId?.toString(),
|
|
488
|
+
nodeAccountIds: transaction.nodeAccountIds?.map((id) => id.toString()) || [],
|
|
489
|
+
maxTransactionFee: transaction.maxTransactionFee?.toTinybars().toString() || "0",
|
|
490
|
+
memo: transaction._transactionMemo || void 0,
|
|
491
|
+
validStart: transaction._transactionValidStart?.toString(),
|
|
492
|
+
validDuration: transaction._transactionValidDuration?.toString()
|
|
493
|
+
};
|
|
419
494
|
}
|
|
420
495
|
/**
|
|
421
|
-
*
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
496
|
+
* Run all available parsers on a transaction
|
|
497
|
+
*/
|
|
498
|
+
static runAllParsers(transaction) {
|
|
499
|
+
return [
|
|
500
|
+
HTSParser.parseFromTransactionObject(transaction),
|
|
501
|
+
CryptoParser.parseFromTransactionObject(transaction),
|
|
502
|
+
HCSParser.parseFromTransactionObject(transaction),
|
|
503
|
+
FileParser.parseFromTransactionObject(transaction),
|
|
504
|
+
SCSParser.parseFromTransactionObject(transaction),
|
|
505
|
+
ScheduleParser.parseFromTransactionObject(transaction),
|
|
506
|
+
UtilParser.parseFromTransactionObject(transaction)
|
|
507
|
+
];
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Parse transaction from protobuf bytes
|
|
426
511
|
*/
|
|
427
|
-
static
|
|
512
|
+
static parseFromProtobuf(bytes) {
|
|
513
|
+
const result = {};
|
|
428
514
|
try {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
515
|
+
const decoded = proto.TransactionList.decode(bytes);
|
|
516
|
+
if (decoded.transactionList && decoded.transactionList.length > 0) {
|
|
517
|
+
const tx = decoded.transactionList[0];
|
|
518
|
+
let txBody = null;
|
|
519
|
+
if (tx.bodyBytes && tx.bodyBytes.length > 0) {
|
|
520
|
+
txBody = proto.TransactionBody.decode(tx.bodyBytes);
|
|
521
|
+
} else if (tx.signedTransactionBytes && tx.signedTransactionBytes.length > 0) {
|
|
522
|
+
const signedTx = proto.SignedTransaction.decode(
|
|
523
|
+
tx.signedTransactionBytes
|
|
524
|
+
);
|
|
525
|
+
if (signedTx.bodyBytes) {
|
|
526
|
+
txBody = proto.TransactionBody.decode(signedTx.bodyBytes);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (txBody) {
|
|
530
|
+
const typeResult = this.detectTransactionTypeFromBody(txBody);
|
|
531
|
+
result.type = typeResult.type;
|
|
532
|
+
result.humanReadableType = typeResult.humanReadableType;
|
|
533
|
+
const details = this.parseTransactionBodyDetails(
|
|
534
|
+
txBody,
|
|
535
|
+
typeResult.type
|
|
536
|
+
);
|
|
537
|
+
Object.assign(result, details);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
} catch (error) {
|
|
432
541
|
}
|
|
542
|
+
return result;
|
|
433
543
|
}
|
|
434
544
|
/**
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
* @param hex - The hex string to convert
|
|
438
|
-
* @returns Uint8Array of bytes
|
|
439
|
-
* @private
|
|
545
|
+
* Intelligently merge all parsing results
|
|
440
546
|
*/
|
|
441
|
-
static
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
547
|
+
static mergeAllResults(result, protoResult, primaryResult, parserResults) {
|
|
548
|
+
const cryptoResult = parserResults.find((r) => r.transfers || r.tokenTransfers) || {};
|
|
549
|
+
result.transfers = cryptoResult.transfers || result.transfers || [];
|
|
550
|
+
result.tokenTransfers = cryptoResult.tokenTransfers || result.tokenTransfers || [];
|
|
551
|
+
Object.keys(protoResult).forEach((key) => {
|
|
552
|
+
if (protoResult[key] !== void 0 && key !== "type" && key !== "humanReadableType") {
|
|
553
|
+
result[key] = protoResult[key];
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
Object.keys(primaryResult).forEach((key) => {
|
|
557
|
+
if (primaryResult[key] !== void 0 && !(key in result) && key !== "type" && key !== "humanReadableType") {
|
|
558
|
+
result[key] = primaryResult[key];
|
|
559
|
+
}
|
|
560
|
+
});
|
|
447
561
|
}
|
|
448
562
|
/**
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
* @param bytes - The bytes to check
|
|
452
|
-
* @param prefix - The prefix to look for
|
|
453
|
-
* @returns True if bytes start with the prefix
|
|
454
|
-
* @private
|
|
563
|
+
* Apply schedulable transaction parsing using registry pattern
|
|
564
|
+
* Eliminates conditional logic in parseTransactionBody
|
|
455
565
|
*/
|
|
456
|
-
static
|
|
457
|
-
if (
|
|
458
|
-
|
|
459
|
-
|
|
566
|
+
static applySchedulableTransactionParsing(txBody, result) {
|
|
567
|
+
if (txBody.cryptoTransfer) {
|
|
568
|
+
CryptoParser.parseCryptoTransfers(txBody.cryptoTransfer, result);
|
|
569
|
+
}
|
|
570
|
+
if (txBody.cryptoDelete) {
|
|
571
|
+
result.cryptoDelete = CryptoParser.parseCryptoDelete(txBody.cryptoDelete);
|
|
572
|
+
}
|
|
573
|
+
if (txBody.cryptoCreateAccount) {
|
|
574
|
+
result.cryptoCreateAccount = CryptoParser.parseCryptoCreateAccount(
|
|
575
|
+
txBody.cryptoCreateAccount
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
if (txBody.cryptoUpdateAccount) {
|
|
579
|
+
result.cryptoUpdateAccount = CryptoParser.parseCryptoUpdateAccount(
|
|
580
|
+
txBody.cryptoUpdateAccount
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
if (txBody.cryptoApproveAllowance) {
|
|
584
|
+
result.cryptoApproveAllowance = CryptoParser.parseCryptoApproveAllowance(
|
|
585
|
+
txBody.cryptoApproveAllowance
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
if (txBody.cryptoDeleteAllowance) {
|
|
589
|
+
result.cryptoDeleteAllowance = CryptoParser.parseCryptoDeleteAllowance(
|
|
590
|
+
txBody.cryptoDeleteAllowance
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
if (txBody.contractCall) {
|
|
594
|
+
result.contractCall = SCSParser.parseContractCall(txBody.contractCall);
|
|
595
|
+
}
|
|
596
|
+
if (txBody.contractCreateInstance) {
|
|
597
|
+
result.contractCreate = SCSParser.parseContractCreate(
|
|
598
|
+
txBody.contractCreateInstance
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
if (txBody.contractUpdateInstance) {
|
|
602
|
+
result.contractUpdate = SCSParser.parseContractUpdate(
|
|
603
|
+
txBody.contractUpdateInstance
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
if (txBody.contractDeleteInstance) {
|
|
607
|
+
result.contractDelete = SCSParser.parseContractDelete(
|
|
608
|
+
txBody.contractDeleteInstance
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
if (txBody.tokenCreation) {
|
|
612
|
+
result.tokenCreation = HTSParser.parseTokenCreate(txBody.tokenCreation);
|
|
613
|
+
}
|
|
614
|
+
if (txBody.tokenMint) {
|
|
615
|
+
result.tokenMint = HTSParser.parseTokenMint(txBody.tokenMint);
|
|
616
|
+
}
|
|
617
|
+
if (txBody.tokenBurn) {
|
|
618
|
+
result.tokenBurn = HTSParser.parseTokenBurn(txBody.tokenBurn);
|
|
619
|
+
}
|
|
620
|
+
if (txBody.tokenUpdate) {
|
|
621
|
+
result.tokenUpdate = HTSParser.parseTokenUpdate(txBody.tokenUpdate);
|
|
622
|
+
}
|
|
623
|
+
if (txBody.tokenFeeScheduleUpdate) {
|
|
624
|
+
result.tokenFeeScheduleUpdate = HTSParser.parseTokenFeeScheduleUpdate(
|
|
625
|
+
txBody.tokenFeeScheduleUpdate
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
if (txBody.tokenFreeze) {
|
|
629
|
+
result.tokenFreeze = HTSParser.parseTokenFreeze(txBody.tokenFreeze);
|
|
630
|
+
}
|
|
631
|
+
if (txBody.tokenUnfreeze) {
|
|
632
|
+
result.tokenUnfreeze = HTSParser.parseTokenUnfreeze(txBody.tokenUnfreeze);
|
|
633
|
+
}
|
|
634
|
+
if (txBody.tokenGrantKyc) {
|
|
635
|
+
result.tokenGrantKyc = HTSParser.parseTokenGrantKyc(txBody.tokenGrantKyc);
|
|
636
|
+
}
|
|
637
|
+
if (txBody.tokenRevokeKyc) {
|
|
638
|
+
result.tokenRevokeKyc = HTSParser.parseTokenRevokeKyc(
|
|
639
|
+
txBody.tokenRevokeKyc
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
if (txBody.tokenPause) {
|
|
643
|
+
result.tokenPause = HTSParser.parseTokenPause(txBody.tokenPause);
|
|
644
|
+
}
|
|
645
|
+
if (txBody.tokenUnpause) {
|
|
646
|
+
result.tokenUnpause = HTSParser.parseTokenUnpause(txBody.tokenUnpause);
|
|
647
|
+
}
|
|
648
|
+
if (txBody.tokenWipe) {
|
|
649
|
+
result.tokenWipeAccount = HTSParser.parseTokenWipeAccount(
|
|
650
|
+
txBody.tokenWipe
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
if (txBody.tokenDeletion) {
|
|
654
|
+
result.tokenDelete = HTSParser.parseTokenDelete(txBody.tokenDeletion);
|
|
655
|
+
}
|
|
656
|
+
if (txBody.tokenAssociate) {
|
|
657
|
+
result.tokenAssociate = HTSParser.parseTokenAssociate(
|
|
658
|
+
txBody.tokenAssociate
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
if (txBody.tokenDissociate) {
|
|
662
|
+
result.tokenDissociate = HTSParser.parseTokenDissociate(
|
|
663
|
+
txBody.tokenDissociate
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
if (txBody.tokenAirdrop) {
|
|
667
|
+
result.tokenAirdrop = HTSParser.parseTokenAirdropFromProto(
|
|
668
|
+
txBody.tokenAirdrop
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
if (txBody.consensusCreateTopic) {
|
|
672
|
+
result.consensusCreateTopic = HCSParser.parseConsensusCreateTopic(
|
|
673
|
+
txBody.consensusCreateTopic
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
if (txBody.consensusSubmitMessage) {
|
|
677
|
+
result.consensusSubmitMessage = HCSParser.parseConsensusSubmitMessage(
|
|
678
|
+
txBody.consensusSubmitMessage
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
if (txBody.consensusUpdateTopic) {
|
|
682
|
+
result.consensusUpdateTopic = HCSParser.parseConsensusUpdateTopic(
|
|
683
|
+
txBody.consensusUpdateTopic
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
if (txBody.consensusDeleteTopic) {
|
|
687
|
+
result.consensusDeleteTopic = HCSParser.parseConsensusDeleteTopic(
|
|
688
|
+
txBody.consensusDeleteTopic
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
if (txBody.fileCreate) {
|
|
692
|
+
result.fileCreate = FileParser.parseFileCreate(txBody.fileCreate);
|
|
693
|
+
}
|
|
694
|
+
if (txBody.fileAppend) {
|
|
695
|
+
result.fileAppend = FileParser.parseFileAppend(txBody.fileAppend);
|
|
696
|
+
}
|
|
697
|
+
if (txBody.fileUpdate) {
|
|
698
|
+
result.fileUpdate = FileParser.parseFileUpdate(txBody.fileUpdate);
|
|
699
|
+
}
|
|
700
|
+
if (txBody.fileDelete) {
|
|
701
|
+
result.fileDelete = FileParser.parseFileDelete(txBody.fileDelete);
|
|
702
|
+
}
|
|
703
|
+
if (txBody.utilPrng) {
|
|
704
|
+
result.utilPrng = UtilParser.parseUtilPrng(txBody.utilPrng);
|
|
460
705
|
}
|
|
461
|
-
return true;
|
|
462
706
|
}
|
|
463
707
|
/**
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
* @param bytes - The bytes to check
|
|
467
|
-
* @returns True if ECDSA private key pattern is found
|
|
468
|
-
* @private
|
|
708
|
+
* Parse transaction body details for all supported transaction types
|
|
709
|
+
* Uses a scalable registry pattern instead of if-else chains
|
|
469
710
|
*/
|
|
470
|
-
static
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
711
|
+
static parseTransactionBodyDetails(txBody, transactionType) {
|
|
712
|
+
const result = {};
|
|
713
|
+
const parserConfig = transactionParserRegistry[transactionType];
|
|
714
|
+
if (parserConfig) {
|
|
715
|
+
const bodyData = txBody[parserConfig.bodyField];
|
|
716
|
+
if (bodyData) {
|
|
717
|
+
const parserResult = parserConfig.parser(bodyData);
|
|
718
|
+
if (parserConfig.spreadResult) {
|
|
719
|
+
Object.assign(result, parserResult);
|
|
720
|
+
} else {
|
|
721
|
+
result[parserConfig.resultField] = parserResult;
|
|
722
|
+
}
|
|
474
723
|
}
|
|
475
724
|
}
|
|
476
|
-
return
|
|
725
|
+
return result;
|
|
477
726
|
}
|
|
478
727
|
/**
|
|
479
|
-
*
|
|
480
|
-
*
|
|
481
|
-
* @param bytes - The DER encoded bytes
|
|
482
|
-
* @returns The extracted private key bytes
|
|
483
|
-
* @private
|
|
728
|
+
* Merge protobuf parsing results with parser results using registry pattern
|
|
729
|
+
* Eliminates transactionFields array and forEach logic
|
|
484
730
|
*/
|
|
485
|
-
static
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
731
|
+
static mergeProtoParsingResults(result, protoResult, htsResult, transaction, originalBytes) {
|
|
732
|
+
const fieldsToMerge = Object.values(transactionParserRegistry).map(
|
|
733
|
+
(config) => config.resultField
|
|
734
|
+
);
|
|
735
|
+
for (const field of fieldsToMerge) {
|
|
736
|
+
const protoValue = protoResult[field];
|
|
737
|
+
const htsValue = htsResult[field];
|
|
738
|
+
if (protoValue !== void 0) {
|
|
739
|
+
result[field] = protoValue;
|
|
740
|
+
} else if (htsValue !== void 0) {
|
|
741
|
+
result[field] = htsValue;
|
|
742
|
+
} else {
|
|
743
|
+
this.handleSpecialFieldExtraction(result, field, transaction);
|
|
489
744
|
}
|
|
490
745
|
}
|
|
491
|
-
return bytes;
|
|
492
746
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
3,
|
|
507
|
-
33,
|
|
508
|
-
0
|
|
509
|
-
]);
|
|
510
|
-
_KeyTypeDetector.ED25519_PRIVATE_KEY_PREFIX = Buffer.from([
|
|
511
|
-
48,
|
|
512
|
-
46,
|
|
513
|
-
2,
|
|
514
|
-
1,
|
|
515
|
-
0,
|
|
516
|
-
48,
|
|
517
|
-
5,
|
|
518
|
-
6,
|
|
519
|
-
3,
|
|
520
|
-
43,
|
|
521
|
-
101,
|
|
522
|
-
112,
|
|
523
|
-
4,
|
|
524
|
-
34,
|
|
525
|
-
4,
|
|
526
|
-
32
|
|
527
|
-
]);
|
|
528
|
-
_KeyTypeDetector.ECDSA_SECP256K1_PUBLIC_KEY_PREFIX = Buffer.from([
|
|
529
|
-
48,
|
|
530
|
-
86,
|
|
531
|
-
48,
|
|
532
|
-
16,
|
|
533
|
-
6,
|
|
534
|
-
7,
|
|
535
|
-
42,
|
|
536
|
-
134,
|
|
537
|
-
72,
|
|
538
|
-
206,
|
|
539
|
-
61,
|
|
540
|
-
2,
|
|
541
|
-
1,
|
|
542
|
-
6,
|
|
543
|
-
5,
|
|
544
|
-
43,
|
|
545
|
-
129,
|
|
546
|
-
4,
|
|
547
|
-
0,
|
|
548
|
-
10,
|
|
549
|
-
3,
|
|
550
|
-
66,
|
|
551
|
-
0
|
|
552
|
-
]);
|
|
553
|
-
_KeyTypeDetector.ECDSA_SECP256K1_PRIVATE_KEY_PREFIX = Buffer.from([
|
|
554
|
-
48,
|
|
555
|
-
116,
|
|
556
|
-
2,
|
|
557
|
-
1,
|
|
558
|
-
1,
|
|
559
|
-
4,
|
|
560
|
-
32
|
|
561
|
-
]);
|
|
562
|
-
_KeyTypeDetector.ECDSA_SECP256K1_PRIVATE_KEY_PREFIX_SHORT = Buffer.from([
|
|
563
|
-
48,
|
|
564
|
-
48,
|
|
565
|
-
2,
|
|
566
|
-
1,
|
|
567
|
-
0,
|
|
568
|
-
48,
|
|
569
|
-
7,
|
|
570
|
-
6,
|
|
571
|
-
5,
|
|
572
|
-
43,
|
|
573
|
-
129,
|
|
574
|
-
4,
|
|
575
|
-
0,
|
|
576
|
-
10,
|
|
577
|
-
4,
|
|
578
|
-
34,
|
|
579
|
-
4,
|
|
580
|
-
32
|
|
581
|
-
]);
|
|
582
|
-
_KeyTypeDetector.ECDSA_SECP256K1_PRIVATE_KEY_PREFIX_LONG = Buffer.from(
|
|
583
|
-
[48, 119, 2, 1, 1, 4, 32]
|
|
584
|
-
);
|
|
585
|
-
let KeyTypeDetector = _KeyTypeDetector;
|
|
586
|
-
function detectKeyTypeFromString(privateKeyString) {
|
|
587
|
-
const keyInfo = KeyTypeDetector.detect(privateKeyString);
|
|
588
|
-
if (keyInfo.type !== "unknown") {
|
|
589
|
-
try {
|
|
590
|
-
const privateKey = keyInfo.type === "ecdsa" ? PrivateKey.fromStringECDSA(privateKeyString) : PrivateKey.fromStringED25519(privateKeyString);
|
|
591
|
-
const result = {
|
|
592
|
-
detectedType: keyInfo.type,
|
|
593
|
-
privateKey
|
|
594
|
-
};
|
|
595
|
-
if (keyInfo.confidence === "uncertain") {
|
|
596
|
-
result.warning = `Key type detection is uncertain. If you have the associated account ID, consider using the Hedera mirror node to confirm the key type.`;
|
|
747
|
+
/**
|
|
748
|
+
* Handle special field extraction cases using registry pattern
|
|
749
|
+
*/
|
|
750
|
+
static handleSpecialFieldExtraction(result, field, transaction) {
|
|
751
|
+
if (field === "tokenCreation" && result.type === "TOKENCREATE") {
|
|
752
|
+
const extracted = HTSParser.extractTokenCreationFromTransaction(transaction);
|
|
753
|
+
if (extracted) {
|
|
754
|
+
result[field] = extracted;
|
|
755
|
+
}
|
|
756
|
+
} else if (field === "tokenAirdrop" && result.type === "TOKENAIRDROP") {
|
|
757
|
+
const extracted = HTSParser.extractTokenAirdropFromTransaction(transaction);
|
|
758
|
+
if (extracted) {
|
|
759
|
+
result[field] = extracted;
|
|
597
760
|
}
|
|
598
|
-
return result;
|
|
599
|
-
} catch (error) {
|
|
600
761
|
}
|
|
601
762
|
}
|
|
602
|
-
|
|
603
|
-
|
|
763
|
+
/**
|
|
764
|
+
* Create fallback result when all parsing methods fail
|
|
765
|
+
*/
|
|
766
|
+
static createFallbackResult(transactionBytes, primaryError, secondaryError) {
|
|
767
|
+
const validation = this.validateTransactionBytes(transactionBytes);
|
|
604
768
|
return {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
769
|
+
type: "UNKNOWN",
|
|
770
|
+
humanReadableType: "Unknown Transaction",
|
|
771
|
+
transfers: [],
|
|
772
|
+
tokenTransfers: [],
|
|
773
|
+
details: {
|
|
774
|
+
rawBytes: transactionBytes.length > 100 ? transactionBytes.substring(0, 100) + "..." : transactionBytes,
|
|
775
|
+
primaryError: primaryError?.message,
|
|
776
|
+
secondaryError: secondaryError?.message,
|
|
777
|
+
parseAttempts: secondaryError ? 2 : 1,
|
|
778
|
+
validation
|
|
779
|
+
},
|
|
780
|
+
formatDetection: {
|
|
781
|
+
originalFormat: validation.format || "base64",
|
|
782
|
+
wasConverted: false,
|
|
783
|
+
length: transactionBytes.length
|
|
784
|
+
},
|
|
785
|
+
raw: {}
|
|
608
786
|
};
|
|
609
|
-
} catch (ecdsaError) {
|
|
610
|
-
try {
|
|
611
|
-
const privateKey = PrivateKey.fromStringED25519(privateKeyString);
|
|
612
|
-
return {
|
|
613
|
-
detectedType: "ed25519",
|
|
614
|
-
privateKey,
|
|
615
|
-
warning: `Using ED25519 as fallback. If you have the associated account ID, consider using the Hedera mirror node to confirm the key type.`
|
|
616
|
-
};
|
|
617
|
-
} catch (ed25519Error) {
|
|
618
|
-
throw new Error(
|
|
619
|
-
`Failed to parse private key as either ECDSA or ED25519: ${ecdsaError}`
|
|
620
|
-
);
|
|
621
|
-
}
|
|
622
787
|
}
|
|
623
788
|
}
|
|
624
789
|
export {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
detectKeyTypeFromString
|
|
790
|
+
TransactionParser,
|
|
791
|
+
TransactionParsingError
|
|
628
792
|
};
|
|
629
793
|
//# sourceMappingURL=standards-sdk.es131.js.map
|