@phantom/parsers 1.0.0-beta.1 → 1.0.0-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @phantom/parsers
2
2
 
3
- A utility package for parsing and converting various message and transaction formats into base64url format for use with Phantom's API. This package provides a unified interface for handling different blockchain transaction formats and message types across multiple networks.
3
+ A utility package for parsing and converting transaction formats into base64url format for use with Phantom's API. This package provides a unified interface for handling different blockchain transaction formats across multiple networks.
4
4
 
5
5
  ## Installation
6
6
 
@@ -12,10 +12,7 @@ yarn add @phantom/parsers
12
12
 
13
13
  ## Overview
14
14
 
15
- The parsers package provides two main functions:
16
-
17
- - **`parseMessage`** - Converts various message formats to base64url
18
- - **`parseTransactionToBase64Url`** - Converts various transaction formats to base64url for different blockchain networks
15
+ - **`parseToKmsTransaction`** - Converts various transaction formats to base64url for different blockchain networks
19
16
 
20
17
  ## Supported Networks
21
18
 
@@ -26,40 +23,9 @@ The parsers package provides two main functions:
26
23
 
27
24
  ## API Reference
28
25
 
29
- ### parseMessage(message)
30
-
31
- Converts various message formats to base64url encoding.
32
-
33
- **Parameters:**
34
-
35
- - `message` (string | Uint8Array | Buffer) - The message to parse
36
-
37
- **Returns:**
38
-
39
- - `ParsedMessage` object with:
40
- - `base64url` (string) - Base64url encoded message
41
- - `originalFormat` (string) - The detected input format
42
-
43
- **Example:**
44
-
45
- ```typescript
46
- import { parseMessage } from "@phantom/parsers";
47
-
48
- // Plain text message
49
- const result1 = parseMessage("Hello, Phantom!");
50
- console.log(result1.base64url); // "SGVsbG8sIFBoYW50b20h"
51
- console.log(result1.originalFormat); // "string"
52
-
53
- // Uint8Array
54
- const bytes = new TextEncoder().encode("Hello, Phantom!");
55
- const result2 = parseMessage(bytes);
56
- console.log(result2.base64url); // "SGVsbG8sIFBoYW50b20h"
57
- console.log(result2.originalFormat); // "bytes"
58
- ```
59
-
60
- ### parseTransactionToBase64Url(transaction, networkId)
26
+ ### parseToKmsTransaction(transaction, networkId)
61
27
 
62
- Converts various transaction formats to base64url encoding based on the target network.
28
+ Converts various transaction formats to different encoding based on the target network.
63
29
 
64
30
  **Parameters:**
65
31
 
@@ -78,25 +44,25 @@ Converts various transaction formats to base64url encoding based on the target n
78
44
 
79
45
  ```typescript
80
46
  import { Transaction } from "@solana/web3.js";
81
- import { parseTransactionToBase64Url } from "@phantom/parsers";
47
+ import { parseToKmsTransaction } from "@phantom/parsers";
82
48
  import { NetworkId } from "@phantom/client";
83
49
 
84
50
  // Solana Web3.js Transaction
85
51
  const transaction = new Transaction().add(/* instructions */);
86
- const result = await parseTransactionToBase64Url(transaction, NetworkId.SOLANA_MAINNET);
52
+ const result = await parseToKmsTransaction(transaction, NetworkId.SOLANA_MAINNET);
87
53
 
88
54
  // Raw bytes
89
55
  const rawBytes = new Uint8Array([1, 2, 3, 4]);
90
- const result2 = await parseTransactionToBase64Url(rawBytes, NetworkId.SOLANA_MAINNET);
56
+ const result2 = await parseToKmsTransaction(rawBytes, NetworkId.SOLANA_MAINNET);
91
57
 
92
58
  // Hex string
93
- const result3 = await parseTransactionToBase64Url("0x01020304", NetworkId.SOLANA_MAINNET);
59
+ const result3 = await parseToKmsTransaction("0x01020304", NetworkId.SOLANA_MAINNET);
94
60
  ```
95
61
 
96
62
  ### Ethereum/EVM
97
63
 
98
64
  ```typescript
99
- import { parseTransactionToBase64Url } from "@phantom/parsers";
65
+ import { parseToKmsTransaction } from "@phantom/parsers";
100
66
  import { NetworkId } from "@phantom/client";
101
67
 
102
68
  // Viem/Ethers transaction object
@@ -108,45 +74,45 @@ const evmTransaction = {
108
74
  gasPrice: 20000000000n, // 20 gwei
109
75
  };
110
76
 
111
- const result = await parseTransactionToBase64Url(evmTransaction, NetworkId.ETHEREUM_MAINNET);
77
+ const result = await parseToKmsTransaction(evmTransaction, NetworkId.ETHEREUM_MAINNET);
112
78
 
113
79
  // Raw transaction bytes
114
80
  const rawTx = new Uint8Array([
115
81
  /* transaction bytes */
116
82
  ]);
117
- const result2 = await parseTransactionToBase64Url(rawTx, NetworkId.ETHEREUM_MAINNET);
83
+ const result2 = await parseToKmsTransaction(rawTx, NetworkId.ETHEREUM_MAINNET);
118
84
 
119
85
  // Hex-encoded transaction
120
- const result3 = await parseTransactionToBase64Url("0xf86c...", NetworkId.ETHEREUM_MAINNET);
86
+ const result3 = await parseToKmsTransaction("0xf86c...", NetworkId.ETHEREUM_MAINNET);
121
87
  ```
122
88
 
123
89
  ### Bitcoin
124
90
 
125
91
  ```typescript
126
- import { parseTransactionToBase64Url } from "@phantom/parsers";
92
+ import { parseToKmsTransaction } from "@phantom/parsers";
127
93
  import { NetworkId } from "@phantom/client";
128
94
 
129
95
  // Raw transaction bytes
130
96
  const bitcoinTx = new Uint8Array([
131
97
  /* bitcoin transaction bytes */
132
98
  ]);
133
- const result = await parseTransactionToBase64Url(bitcoinTx, NetworkId.BITCOIN_MAINNET);
99
+ const result = await parseToKmsTransaction(bitcoinTx, NetworkId.BITCOIN_MAINNET);
134
100
 
135
101
  // Hex-encoded transaction
136
- const result2 = await parseTransactionToBase64Url("0x0100000001...", NetworkId.BITCOIN_MAINNET);
102
+ const result2 = await parseToKmsTransaction("0x0100000001...", NetworkId.BITCOIN_MAINNET);
137
103
  ```
138
104
 
139
105
  ### Sui
140
106
 
141
107
  ```typescript
142
- import { parseTransactionToBase64Url } from "@phantom/parsers";
108
+ import { parseToKmsTransaction } from "@phantom/parsers";
143
109
  import { NetworkId } from "@phantom/client";
144
110
 
145
111
  // Sui transaction bytes
146
112
  const suiTx = new Uint8Array([
147
113
  /* sui transaction bytes */
148
114
  ]);
149
- const result = await parseTransactionToBase64Url(suiTx, NetworkId.SUI_MAINNET);
115
+ const result = await parseToKmsTransaction(suiTx, NetworkId.SUI_MAINNET);
150
116
  ```
151
117
 
152
118
  ## Format Detection
@@ -184,7 +150,7 @@ The parsers will throw descriptive errors for:
184
150
 
185
151
  ```typescript
186
152
  try {
187
- const result = await parseTransactionToBase64Url(invalidData, "unsupported:network");
153
+ const result = await parseToKmsTransaction(invalidData, "unsupported:network");
188
154
  } catch (error) {
189
155
  console.error("Parsing failed:", error.message);
190
156
  // "Unsupported network: unsupported"
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { NetworkId } from '@phantom/constants';
2
- import { Transaction } from '@solana/transactions';
2
+ import { Transaction as Transaction$1 } from '@solana/transactions';
3
+ import { Transaction, VersionedTransaction } from '@solana/web3.js';
3
4
 
4
5
  /**
5
6
  * Chain-specific transaction and message response parsing
@@ -22,25 +23,34 @@ interface ParsedTransactionResult {
22
23
  declare function parseSignMessageResponse(base64Response: string, networkId: NetworkId): ParsedSignatureResult;
23
24
  /**
24
25
  * Parse a transaction response from base64 rawTransaction to extract hash
26
+ * For Ethereum chains, converts base64url to hex format
25
27
  */
26
28
  declare function parseTransactionResponse(base64RawTransaction: string, networkId: NetworkId, hash?: string): ParsedTransactionResult;
29
+ /**
30
+ * Parse Solana signed transaction from base64url encoded transaction bytes
31
+ * Supports both legacy Transaction and VersionedTransaction formats
32
+ */
33
+ declare function parseSolanaSignedTransaction(base64RawTransaction: string): Transaction | VersionedTransaction | null;
34
+ /**
35
+ * Deserialize Solana transaction from Uint8Array bytes
36
+ * Supports both legacy Transaction and VersionedTransaction formats
37
+ */
38
+ declare function deserializeSolanaTransaction(transactionBytes: Uint8Array): Transaction | VersionedTransaction;
27
39
 
28
40
  interface ParsedTransaction {
29
- base64url: string;
41
+ /** The parsed transaction string (base64url for Solana/Sui/Bitcoin, RLP-encoded hex for EVM) */
42
+ parsed?: string;
43
+ /** Original format of the input transaction */
30
44
  originalFormat: string;
31
45
  }
32
- interface ParsedMessage {
33
- base64url: string;
34
- }
35
46
 
36
47
  /**
37
- * Parse a message to base64url format for the client
38
- */
39
- declare function parseMessage(message: string): ParsedMessage;
40
- /**
41
- * Parse a transaction to base64url format based on network type
48
+ * Parse a transaction to KMS format based on network type
49
+ * - Solana: base64url encoding
50
+ * - EVM chains: hex encoding
51
+ * - Sui, Bitcoin: base64url encoding
42
52
  */
43
- declare function parseTransactionToBase64Url(transaction: any, networkId: NetworkId): Promise<ParsedTransaction>;
44
- declare function parseSolanaKitTransactionToSolanaWeb3js(transaction: Transaction): any;
53
+ declare function parseToKmsTransaction(transaction: any, networkId: NetworkId): Promise<ParsedTransaction>;
54
+ declare function parseSolanaKitTransactionToSolanaWeb3js(transaction: Transaction$1): any;
45
55
 
46
- export { ParsedMessage, ParsedSignatureResult, ParsedTransaction, ParsedTransactionResult, parseMessage, parseSignMessageResponse, parseSolanaKitTransactionToSolanaWeb3js, parseTransactionResponse, parseTransactionToBase64Url };
56
+ export { ParsedSignatureResult, ParsedTransaction, ParsedTransactionResult, deserializeSolanaTransaction, parseSignMessageResponse, parseSolanaKitTransactionToSolanaWeb3js, parseSolanaSignedTransaction, parseToKmsTransaction, parseTransactionResponse };
package/dist/index.js CHANGED
@@ -30,22 +30,26 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
- parseMessage: () => parseMessage,
33
+ deserializeSolanaTransaction: () => deserializeSolanaTransaction,
34
34
  parseSignMessageResponse: () => parseSignMessageResponse,
35
35
  parseSolanaKitTransactionToSolanaWeb3js: () => parseSolanaKitTransactionToSolanaWeb3js,
36
- parseTransactionResponse: () => parseTransactionResponse,
37
- parseTransactionToBase64Url: () => parseTransactionToBase64Url
36
+ parseSolanaSignedTransaction: () => parseSolanaSignedTransaction,
37
+ parseToKmsTransaction: () => parseToKmsTransaction,
38
+ parseTransactionResponse: () => parseTransactionResponse
38
39
  });
39
40
  module.exports = __toCommonJS(src_exports);
40
41
  var import_base64url2 = require("@phantom/base64url");
41
42
  var import_transactions = require("@solana/transactions");
42
43
  var import_buffer2 = require("buffer");
44
+ var import_ethers = require("ethers");
43
45
 
44
46
  // src/response-parsers.ts
45
- var import_constants = require("@phantom/constants");
46
47
  var import_base64url = require("@phantom/base64url");
47
- var import_buffer = require("buffer");
48
+ var import_constants = require("@phantom/constants");
49
+ var import_utils = require("@phantom/utils");
50
+ var import_web3 = require("@solana/web3.js");
48
51
  var import_bs58 = __toESM(require("bs58"));
52
+ var import_buffer = require("buffer");
49
53
  function parseSignMessageResponse(base64Response, networkId) {
50
54
  const networkPrefix = networkId.split(":")[0].toLowerCase();
51
55
  switch (networkPrefix) {
@@ -67,15 +71,24 @@ function parseSignMessageResponse(base64Response, networkId) {
67
71
  }
68
72
  }
69
73
  function parseTransactionResponse(base64RawTransaction, networkId, hash) {
74
+ let rawTransaction = base64RawTransaction;
75
+ if ((0, import_utils.isEthereumChain)(networkId)) {
76
+ try {
77
+ const txBytes = (0, import_base64url.base64urlDecode)(base64RawTransaction);
78
+ rawTransaction = "0x" + import_buffer.Buffer.from(txBytes).toString("hex");
79
+ } catch (error) {
80
+ rawTransaction = base64RawTransaction.startsWith("0x") ? base64RawTransaction : "0x" + base64RawTransaction;
81
+ }
82
+ }
70
83
  if (hash) {
71
84
  return {
72
85
  hash,
73
- rawTransaction: base64RawTransaction,
86
+ rawTransaction,
74
87
  blockExplorer: (0, import_constants.getExplorerUrl)(networkId, "transaction", hash)
75
88
  };
76
89
  } else {
77
90
  return {
78
- rawTransaction: base64RawTransaction
91
+ rawTransaction
79
92
  };
80
93
  }
81
94
  }
@@ -95,21 +108,12 @@ function parseSolanaSignatureResponse(base64Response) {
95
108
  }
96
109
  }
97
110
  function parseEVMSignatureResponse(base64Response) {
98
- try {
99
- const signatureBytes = (0, import_base64url.base64urlDecode)(base64Response);
100
- const signature = "0x" + import_buffer.Buffer.from(signatureBytes).toString("hex");
101
- return {
102
- signature,
103
- rawSignature: base64Response
104
- // Note: Most block explorers don't have direct signature lookup, only transaction lookup
105
- };
106
- } catch (error) {
107
- const signature = base64Response.startsWith("0x") ? base64Response : "0x" + base64Response;
108
- return {
109
- signature,
110
- rawSignature: base64Response
111
- };
112
- }
111
+ const signatureBytes = (0, import_base64url.base64urlDecode)(base64Response);
112
+ const signature = "0x" + import_buffer.Buffer.from(signatureBytes).toString("hex");
113
+ return {
114
+ signature,
115
+ rawSignature: base64Response
116
+ };
113
117
  }
114
118
  function parseSuiSignatureResponse(base64Response) {
115
119
  try {
@@ -141,14 +145,29 @@ function parseBitcoinSignatureResponse(base64Response) {
141
145
  };
142
146
  }
143
147
  }
148
+ function parseSolanaSignedTransaction(base64RawTransaction) {
149
+ try {
150
+ const transactionBytes = (0, import_base64url.base64urlDecode)(base64RawTransaction);
151
+ return deserializeSolanaTransaction(transactionBytes);
152
+ } catch (error) {
153
+ return null;
154
+ }
155
+ }
156
+ function deserializeSolanaTransaction(transactionBytes) {
157
+ try {
158
+ const transaction = import_web3.Transaction.from(transactionBytes);
159
+ return transaction;
160
+ } catch (legacyError) {
161
+ if (legacyError instanceof Error && legacyError.message.includes("Versioned messages")) {
162
+ const versionedTransaction = import_web3.VersionedTransaction.deserialize(transactionBytes);
163
+ return versionedTransaction;
164
+ }
165
+ throw legacyError;
166
+ }
167
+ }
144
168
 
145
169
  // src/index.ts
146
- function parseMessage(message) {
147
- return {
148
- base64url: (0, import_base64url2.stringToBase64url)(message)
149
- };
150
- }
151
- async function parseTransactionToBase64Url(transaction, networkId) {
170
+ async function parseToKmsTransaction(transaction, networkId) {
152
171
  const networkPrefix = networkId.split(":")[0].toLowerCase();
153
172
  switch (networkPrefix) {
154
173
  case "solana":
@@ -159,7 +178,7 @@ async function parseTransactionToBase64Url(transaction, networkId) {
159
178
  case "optimism":
160
179
  case "arbitrum":
161
180
  case "base":
162
- return parseEVMTransactionToBase64Url(transaction);
181
+ return parseEVMTransactionToHex(transaction);
163
182
  case "sui":
164
183
  return await parseSuiTransactionToBase64Url(transaction);
165
184
  case "bitcoin":
@@ -171,7 +190,7 @@ async function parseTransactionToBase64Url(transaction, networkId) {
171
190
  function parseSolanaTransactionToBase64Url(transaction) {
172
191
  if (transaction?.messageBytes != null) {
173
192
  return {
174
- base64url: (0, import_base64url2.base64urlEncode)(transaction.messageBytes),
193
+ parsed: (0, import_base64url2.base64urlEncode)(transaction.messageBytes),
175
194
  originalFormat: "@solana/kit"
176
195
  };
177
196
  }
@@ -181,13 +200,13 @@ function parseSolanaTransactionToBase64Url(transaction) {
181
200
  verifySignatures: false
182
201
  });
183
202
  return {
184
- base64url: (0, import_base64url2.base64urlEncode)(serialized),
203
+ parsed: (0, import_base64url2.base64urlEncode)(serialized),
185
204
  originalFormat: "@solana/web3.js"
186
205
  };
187
206
  }
188
207
  if (transaction instanceof Uint8Array) {
189
208
  return {
190
- base64url: (0, import_base64url2.base64urlEncode)(transaction),
209
+ parsed: (0, import_base64url2.base64urlEncode)(transaction),
191
210
  originalFormat: "bytes"
192
211
  };
193
212
  }
@@ -195,7 +214,7 @@ function parseSolanaTransactionToBase64Url(transaction) {
195
214
  try {
196
215
  const bytes = import_buffer2.Buffer.from(transaction, "base64");
197
216
  return {
198
- base64url: (0, import_base64url2.base64urlEncode)(new Uint8Array(bytes)),
217
+ parsed: (0, import_base64url2.base64urlEncode)(new Uint8Array(bytes)),
199
218
  originalFormat: "base64"
200
219
  };
201
220
  } catch {
@@ -204,50 +223,79 @@ function parseSolanaTransactionToBase64Url(transaction) {
204
223
  }
205
224
  throw new Error("Unsupported Solana transaction format");
206
225
  }
207
- function parseEVMTransactionToBase64Url(transaction) {
208
- if (transaction && typeof transaction === "object" && (transaction.to || transaction.data)) {
209
- const bytes = new TextEncoder().encode(
210
- JSON.stringify(transaction, (_key, value) => typeof value === "bigint" ? value.toString() : value)
211
- );
226
+ function parseEVMTransactionToHex(transaction) {
227
+ if (typeof transaction === "string" && transaction.startsWith("0x")) {
212
228
  return {
213
- base64url: (0, import_base64url2.base64urlEncode)(bytes),
214
- originalFormat: "viem"
229
+ parsed: transaction,
230
+ originalFormat: "hex"
215
231
  };
216
232
  }
217
233
  if (transaction?.serialize && typeof transaction.serialize === "function") {
218
234
  const serialized = transaction.serialize();
219
- const bytes = new Uint8Array(import_buffer2.Buffer.from(serialized.slice(2), "hex"));
235
+ const hex = serialized.startsWith("0x") ? serialized : "0x" + serialized;
220
236
  return {
221
- base64url: (0, import_base64url2.base64urlEncode)(bytes),
237
+ parsed: hex,
222
238
  originalFormat: "ethers"
223
239
  };
224
240
  }
225
241
  if (transaction instanceof Uint8Array) {
242
+ const hex = "0x" + import_buffer2.Buffer.from(transaction).toString("hex");
226
243
  return {
227
- base64url: (0, import_base64url2.base64urlEncode)(transaction),
244
+ parsed: hex,
228
245
  originalFormat: "bytes"
229
246
  };
230
247
  }
231
- if (typeof transaction === "string" && transaction.startsWith("0x")) {
232
- const bytes = new Uint8Array(import_buffer2.Buffer.from(transaction.slice(2), "hex"));
233
- return {
234
- base64url: (0, import_base64url2.base64urlEncode)(bytes),
235
- originalFormat: "hex"
236
- };
248
+ if (transaction && typeof transaction === "object" && (transaction.to || transaction.data || transaction.from)) {
249
+ try {
250
+ const { from, gas, ...txForSerialization } = transaction;
251
+ if (gas) {
252
+ txForSerialization.gasLimit = gas;
253
+ }
254
+ if (!txForSerialization.gasLimit) {
255
+ if (txForSerialization.to && txForSerialization.value && !txForSerialization.data) {
256
+ txForSerialization.gasLimit = "0x5208";
257
+ }
258
+ }
259
+ if (txForSerialization.to && typeof txForSerialization.to === "string") {
260
+ try {
261
+ txForSerialization.to = (0, import_ethers.getAddress)(txForSerialization.to);
262
+ } catch {
263
+ txForSerialization.to = txForSerialization.to.toLowerCase();
264
+ }
265
+ }
266
+ const serialized = import_ethers.Transaction.from(txForSerialization).unsignedSerialized;
267
+ const hex = serialized.startsWith("0x") ? serialized : "0x" + serialized;
268
+ return {
269
+ parsed: hex,
270
+ originalFormat: "json"
271
+ };
272
+ } catch (error) {
273
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
274
+ const txKeys = transaction ? Object.keys(transaction).join(", ") : "N/A";
275
+ const txValues = transaction ? JSON.stringify(transaction, null, 2) : "N/A";
276
+ throw new Error(
277
+ `Failed to RLP encode EVM transaction: ${errorMessage}.
278
+ Transaction keys: [${txKeys}].
279
+ Transaction: ${txValues}
280
+ Please ensure the transaction object includes required fields (to, value, chainId, gasLimit or gasPrice, etc.)`
281
+ );
282
+ }
237
283
  }
238
- throw new Error("Unsupported EVM transaction format");
284
+ throw new Error(
285
+ "Unsupported EVM transaction format. Expected hex string, bytes, or transaction object with 'to', 'data', or 'from' fields."
286
+ );
239
287
  }
240
288
  async function parseSuiTransactionToBase64Url(transaction) {
241
289
  if (transaction?.serialize && typeof transaction.serialize === "function") {
242
290
  const serialized = transaction.serialize();
243
291
  return {
244
- base64url: (0, import_base64url2.base64urlEncode)(serialized),
292
+ parsed: (0, import_base64url2.base64urlEncode)(serialized),
245
293
  originalFormat: "sui-sdk"
246
294
  };
247
295
  }
248
296
  if (transaction instanceof Uint8Array) {
249
297
  return {
250
- base64url: (0, import_base64url2.base64urlEncode)(transaction),
298
+ parsed: (0, import_base64url2.base64urlEncode)(transaction),
251
299
  originalFormat: "bytes"
252
300
  };
253
301
  }
@@ -256,7 +304,7 @@ async function parseSuiTransactionToBase64Url(transaction) {
256
304
  if (built?.serialize && typeof built.serialize === "function") {
257
305
  const serialized = built.serialize();
258
306
  return {
259
- base64url: (0, import_base64url2.base64urlEncode)(serialized),
307
+ parsed: (0, import_base64url2.base64urlEncode)(serialized),
260
308
  originalFormat: "transaction-block"
261
309
  };
262
310
  }
@@ -267,20 +315,20 @@ function parseBitcoinTransactionToBase64Url(transaction) {
267
315
  if (transaction?.toBuffer && typeof transaction.toBuffer === "function") {
268
316
  const buffer = transaction.toBuffer();
269
317
  return {
270
- base64url: (0, import_base64url2.base64urlEncode)(new Uint8Array(buffer)),
318
+ parsed: (0, import_base64url2.base64urlEncode)(new Uint8Array(buffer)),
271
319
  originalFormat: "bitcoinjs-lib"
272
320
  };
273
321
  }
274
322
  if (transaction instanceof Uint8Array) {
275
323
  return {
276
- base64url: (0, import_base64url2.base64urlEncode)(transaction),
324
+ parsed: (0, import_base64url2.base64urlEncode)(transaction),
277
325
  originalFormat: "bytes"
278
326
  };
279
327
  }
280
328
  if (typeof transaction === "string") {
281
329
  const bytes = new Uint8Array(import_buffer2.Buffer.from(transaction, "hex"));
282
330
  return {
283
- base64url: (0, import_base64url2.base64urlEncode)(bytes),
331
+ parsed: (0, import_base64url2.base64urlEncode)(bytes),
284
332
  originalFormat: "hex"
285
333
  };
286
334
  }
@@ -297,9 +345,10 @@ function parseSolanaKitTransactionToSolanaWeb3js(transaction) {
297
345
  }
298
346
  // Annotate the CommonJS export names for ESM import in node:
299
347
  0 && (module.exports = {
300
- parseMessage,
348
+ deserializeSolanaTransaction,
301
349
  parseSignMessageResponse,
302
350
  parseSolanaKitTransactionToSolanaWeb3js,
303
- parseTransactionResponse,
304
- parseTransactionToBase64Url
351
+ parseSolanaSignedTransaction,
352
+ parseToKmsTransaction,
353
+ parseTransactionResponse
305
354
  });
package/dist/index.mjs CHANGED
@@ -1,13 +1,16 @@
1
1
  // src/index.ts
2
- import { base64urlEncode, stringToBase64url } from "@phantom/base64url";
2
+ import { base64urlEncode } from "@phantom/base64url";
3
3
  import { getTransactionEncoder } from "@solana/transactions";
4
4
  import { Buffer as Buffer2 } from "buffer";
5
+ import { Transaction as EthersTransaction, getAddress } from "ethers";
5
6
 
6
7
  // src/response-parsers.ts
7
- import { getExplorerUrl } from "@phantom/constants";
8
8
  import { base64urlDecode } from "@phantom/base64url";
9
- import { Buffer } from "buffer";
9
+ import { getExplorerUrl } from "@phantom/constants";
10
+ import { isEthereumChain } from "@phantom/utils";
11
+ import { Transaction, VersionedTransaction } from "@solana/web3.js";
10
12
  import bs58 from "bs58";
13
+ import { Buffer } from "buffer";
11
14
  function parseSignMessageResponse(base64Response, networkId) {
12
15
  const networkPrefix = networkId.split(":")[0].toLowerCase();
13
16
  switch (networkPrefix) {
@@ -29,15 +32,24 @@ function parseSignMessageResponse(base64Response, networkId) {
29
32
  }
30
33
  }
31
34
  function parseTransactionResponse(base64RawTransaction, networkId, hash) {
35
+ let rawTransaction = base64RawTransaction;
36
+ if (isEthereumChain(networkId)) {
37
+ try {
38
+ const txBytes = base64urlDecode(base64RawTransaction);
39
+ rawTransaction = "0x" + Buffer.from(txBytes).toString("hex");
40
+ } catch (error) {
41
+ rawTransaction = base64RawTransaction.startsWith("0x") ? base64RawTransaction : "0x" + base64RawTransaction;
42
+ }
43
+ }
32
44
  if (hash) {
33
45
  return {
34
46
  hash,
35
- rawTransaction: base64RawTransaction,
47
+ rawTransaction,
36
48
  blockExplorer: getExplorerUrl(networkId, "transaction", hash)
37
49
  };
38
50
  } else {
39
51
  return {
40
- rawTransaction: base64RawTransaction
52
+ rawTransaction
41
53
  };
42
54
  }
43
55
  }
@@ -57,21 +69,12 @@ function parseSolanaSignatureResponse(base64Response) {
57
69
  }
58
70
  }
59
71
  function parseEVMSignatureResponse(base64Response) {
60
- try {
61
- const signatureBytes = base64urlDecode(base64Response);
62
- const signature = "0x" + Buffer.from(signatureBytes).toString("hex");
63
- return {
64
- signature,
65
- rawSignature: base64Response
66
- // Note: Most block explorers don't have direct signature lookup, only transaction lookup
67
- };
68
- } catch (error) {
69
- const signature = base64Response.startsWith("0x") ? base64Response : "0x" + base64Response;
70
- return {
71
- signature,
72
- rawSignature: base64Response
73
- };
74
- }
72
+ const signatureBytes = base64urlDecode(base64Response);
73
+ const signature = "0x" + Buffer.from(signatureBytes).toString("hex");
74
+ return {
75
+ signature,
76
+ rawSignature: base64Response
77
+ };
75
78
  }
76
79
  function parseSuiSignatureResponse(base64Response) {
77
80
  try {
@@ -103,14 +106,29 @@ function parseBitcoinSignatureResponse(base64Response) {
103
106
  };
104
107
  }
105
108
  }
109
+ function parseSolanaSignedTransaction(base64RawTransaction) {
110
+ try {
111
+ const transactionBytes = base64urlDecode(base64RawTransaction);
112
+ return deserializeSolanaTransaction(transactionBytes);
113
+ } catch (error) {
114
+ return null;
115
+ }
116
+ }
117
+ function deserializeSolanaTransaction(transactionBytes) {
118
+ try {
119
+ const transaction = Transaction.from(transactionBytes);
120
+ return transaction;
121
+ } catch (legacyError) {
122
+ if (legacyError instanceof Error && legacyError.message.includes("Versioned messages")) {
123
+ const versionedTransaction = VersionedTransaction.deserialize(transactionBytes);
124
+ return versionedTransaction;
125
+ }
126
+ throw legacyError;
127
+ }
128
+ }
106
129
 
107
130
  // src/index.ts
108
- function parseMessage(message) {
109
- return {
110
- base64url: stringToBase64url(message)
111
- };
112
- }
113
- async function parseTransactionToBase64Url(transaction, networkId) {
131
+ async function parseToKmsTransaction(transaction, networkId) {
114
132
  const networkPrefix = networkId.split(":")[0].toLowerCase();
115
133
  switch (networkPrefix) {
116
134
  case "solana":
@@ -121,7 +139,7 @@ async function parseTransactionToBase64Url(transaction, networkId) {
121
139
  case "optimism":
122
140
  case "arbitrum":
123
141
  case "base":
124
- return parseEVMTransactionToBase64Url(transaction);
142
+ return parseEVMTransactionToHex(transaction);
125
143
  case "sui":
126
144
  return await parseSuiTransactionToBase64Url(transaction);
127
145
  case "bitcoin":
@@ -133,7 +151,7 @@ async function parseTransactionToBase64Url(transaction, networkId) {
133
151
  function parseSolanaTransactionToBase64Url(transaction) {
134
152
  if (transaction?.messageBytes != null) {
135
153
  return {
136
- base64url: base64urlEncode(transaction.messageBytes),
154
+ parsed: base64urlEncode(transaction.messageBytes),
137
155
  originalFormat: "@solana/kit"
138
156
  };
139
157
  }
@@ -143,13 +161,13 @@ function parseSolanaTransactionToBase64Url(transaction) {
143
161
  verifySignatures: false
144
162
  });
145
163
  return {
146
- base64url: base64urlEncode(serialized),
164
+ parsed: base64urlEncode(serialized),
147
165
  originalFormat: "@solana/web3.js"
148
166
  };
149
167
  }
150
168
  if (transaction instanceof Uint8Array) {
151
169
  return {
152
- base64url: base64urlEncode(transaction),
170
+ parsed: base64urlEncode(transaction),
153
171
  originalFormat: "bytes"
154
172
  };
155
173
  }
@@ -157,7 +175,7 @@ function parseSolanaTransactionToBase64Url(transaction) {
157
175
  try {
158
176
  const bytes = Buffer2.from(transaction, "base64");
159
177
  return {
160
- base64url: base64urlEncode(new Uint8Array(bytes)),
178
+ parsed: base64urlEncode(new Uint8Array(bytes)),
161
179
  originalFormat: "base64"
162
180
  };
163
181
  } catch {
@@ -166,50 +184,79 @@ function parseSolanaTransactionToBase64Url(transaction) {
166
184
  }
167
185
  throw new Error("Unsupported Solana transaction format");
168
186
  }
169
- function parseEVMTransactionToBase64Url(transaction) {
170
- if (transaction && typeof transaction === "object" && (transaction.to || transaction.data)) {
171
- const bytes = new TextEncoder().encode(
172
- JSON.stringify(transaction, (_key, value) => typeof value === "bigint" ? value.toString() : value)
173
- );
187
+ function parseEVMTransactionToHex(transaction) {
188
+ if (typeof transaction === "string" && transaction.startsWith("0x")) {
174
189
  return {
175
- base64url: base64urlEncode(bytes),
176
- originalFormat: "viem"
190
+ parsed: transaction,
191
+ originalFormat: "hex"
177
192
  };
178
193
  }
179
194
  if (transaction?.serialize && typeof transaction.serialize === "function") {
180
195
  const serialized = transaction.serialize();
181
- const bytes = new Uint8Array(Buffer2.from(serialized.slice(2), "hex"));
196
+ const hex = serialized.startsWith("0x") ? serialized : "0x" + serialized;
182
197
  return {
183
- base64url: base64urlEncode(bytes),
198
+ parsed: hex,
184
199
  originalFormat: "ethers"
185
200
  };
186
201
  }
187
202
  if (transaction instanceof Uint8Array) {
203
+ const hex = "0x" + Buffer2.from(transaction).toString("hex");
188
204
  return {
189
- base64url: base64urlEncode(transaction),
205
+ parsed: hex,
190
206
  originalFormat: "bytes"
191
207
  };
192
208
  }
193
- if (typeof transaction === "string" && transaction.startsWith("0x")) {
194
- const bytes = new Uint8Array(Buffer2.from(transaction.slice(2), "hex"));
195
- return {
196
- base64url: base64urlEncode(bytes),
197
- originalFormat: "hex"
198
- };
209
+ if (transaction && typeof transaction === "object" && (transaction.to || transaction.data || transaction.from)) {
210
+ try {
211
+ const { from, gas, ...txForSerialization } = transaction;
212
+ if (gas) {
213
+ txForSerialization.gasLimit = gas;
214
+ }
215
+ if (!txForSerialization.gasLimit) {
216
+ if (txForSerialization.to && txForSerialization.value && !txForSerialization.data) {
217
+ txForSerialization.gasLimit = "0x5208";
218
+ }
219
+ }
220
+ if (txForSerialization.to && typeof txForSerialization.to === "string") {
221
+ try {
222
+ txForSerialization.to = getAddress(txForSerialization.to);
223
+ } catch {
224
+ txForSerialization.to = txForSerialization.to.toLowerCase();
225
+ }
226
+ }
227
+ const serialized = EthersTransaction.from(txForSerialization).unsignedSerialized;
228
+ const hex = serialized.startsWith("0x") ? serialized : "0x" + serialized;
229
+ return {
230
+ parsed: hex,
231
+ originalFormat: "json"
232
+ };
233
+ } catch (error) {
234
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
235
+ const txKeys = transaction ? Object.keys(transaction).join(", ") : "N/A";
236
+ const txValues = transaction ? JSON.stringify(transaction, null, 2) : "N/A";
237
+ throw new Error(
238
+ `Failed to RLP encode EVM transaction: ${errorMessage}.
239
+ Transaction keys: [${txKeys}].
240
+ Transaction: ${txValues}
241
+ Please ensure the transaction object includes required fields (to, value, chainId, gasLimit or gasPrice, etc.)`
242
+ );
243
+ }
199
244
  }
200
- throw new Error("Unsupported EVM transaction format");
245
+ throw new Error(
246
+ "Unsupported EVM transaction format. Expected hex string, bytes, or transaction object with 'to', 'data', or 'from' fields."
247
+ );
201
248
  }
202
249
  async function parseSuiTransactionToBase64Url(transaction) {
203
250
  if (transaction?.serialize && typeof transaction.serialize === "function") {
204
251
  const serialized = transaction.serialize();
205
252
  return {
206
- base64url: base64urlEncode(serialized),
253
+ parsed: base64urlEncode(serialized),
207
254
  originalFormat: "sui-sdk"
208
255
  };
209
256
  }
210
257
  if (transaction instanceof Uint8Array) {
211
258
  return {
212
- base64url: base64urlEncode(transaction),
259
+ parsed: base64urlEncode(transaction),
213
260
  originalFormat: "bytes"
214
261
  };
215
262
  }
@@ -218,7 +265,7 @@ async function parseSuiTransactionToBase64Url(transaction) {
218
265
  if (built?.serialize && typeof built.serialize === "function") {
219
266
  const serialized = built.serialize();
220
267
  return {
221
- base64url: base64urlEncode(serialized),
268
+ parsed: base64urlEncode(serialized),
222
269
  originalFormat: "transaction-block"
223
270
  };
224
271
  }
@@ -229,20 +276,20 @@ function parseBitcoinTransactionToBase64Url(transaction) {
229
276
  if (transaction?.toBuffer && typeof transaction.toBuffer === "function") {
230
277
  const buffer = transaction.toBuffer();
231
278
  return {
232
- base64url: base64urlEncode(new Uint8Array(buffer)),
279
+ parsed: base64urlEncode(new Uint8Array(buffer)),
233
280
  originalFormat: "bitcoinjs-lib"
234
281
  };
235
282
  }
236
283
  if (transaction instanceof Uint8Array) {
237
284
  return {
238
- base64url: base64urlEncode(transaction),
285
+ parsed: base64urlEncode(transaction),
239
286
  originalFormat: "bytes"
240
287
  };
241
288
  }
242
289
  if (typeof transaction === "string") {
243
290
  const bytes = new Uint8Array(Buffer2.from(transaction, "hex"));
244
291
  return {
245
- base64url: base64urlEncode(bytes),
292
+ parsed: base64urlEncode(bytes),
246
293
  originalFormat: "hex"
247
294
  };
248
295
  }
@@ -258,9 +305,10 @@ function parseSolanaKitTransactionToSolanaWeb3js(transaction) {
258
305
  return fakeVersioned;
259
306
  }
260
307
  export {
261
- parseMessage,
308
+ deserializeSolanaTransaction,
262
309
  parseSignMessageResponse,
263
310
  parseSolanaKitTransactionToSolanaWeb3js,
264
- parseTransactionResponse,
265
- parseTransactionToBase64Url
311
+ parseSolanaSignedTransaction,
312
+ parseToKmsTransaction,
313
+ parseTransactionResponse
266
314
  };
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@phantom/parsers",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.12",
4
4
  "description": "Transaction and message parsers for Phantom Wallet SDK",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/phantom/phantom-connect-sdk",
8
+ "directory": "packages/parsers"
9
+ },
5
10
  "main": "dist/index.js",
6
11
  "module": "dist/index.mjs",
7
12
  "types": "dist/index.d.ts",
@@ -29,21 +34,24 @@
29
34
  "prettier": "prettier --write \"src/**/*.{ts}\""
30
35
  },
31
36
  "dependencies": {
32
- "@phantom/base64url": "^1.0.0-beta.1",
33
- "@phantom/constants": "^1.0.0-beta.1",
34
- "@solana/kit": "^2.1.1",
37
+ "@phantom/base64url": "^1.0.0-beta.12",
38
+ "@phantom/constants": "^1.0.0-beta.12",
39
+ "@phantom/sdk-types": "^1.0.0-beta.12",
40
+ "@phantom/utils": "^1.0.0-beta.24",
35
41
  "@solana/transactions": "^2.0.0",
42
+ "@solana/web3.js": "^1.95.0",
36
43
  "bs58": "^6.0.0",
37
- "buffer": "^6.0.3"
44
+ "buffer": "^6.0.3",
45
+ "ethers": "^6.0.0"
38
46
  },
39
47
  "optionalDependencies": {
40
48
  "@mysten/sui.js": "^0.50.0",
41
- "@solana/web3.js": "^1.95.0",
42
49
  "bitcoinjs-lib": "^6.1.0",
43
- "ethers": "^6.0.0",
44
50
  "viem": "^2.0.0"
45
51
  },
46
52
  "devDependencies": {
53
+ "@solana/kit": "^2.1.1",
54
+ "@solana/web3.js": "^1.95.0",
47
55
  "@types/jest": "^29.5.12",
48
56
  "@types/node": "^20.11.0",
49
57
  "dotenv": "^16.4.5",