@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 +18 -52
- package/dist/index.d.ts +23 -13
- package/dist/index.js +108 -59
- package/dist/index.mjs +105 -57
- package/package.json +15 -7
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @phantom/parsers
|
|
2
2
|
|
|
3
|
-
A utility package for parsing and converting
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
56
|
+
const result2 = await parseToKmsTransaction(rawBytes, NetworkId.SOLANA_MAINNET);
|
|
91
57
|
|
|
92
58
|
// Hex string
|
|
93
|
-
const result3 = await
|
|
59
|
+
const result3 = await parseToKmsTransaction("0x01020304", NetworkId.SOLANA_MAINNET);
|
|
94
60
|
```
|
|
95
61
|
|
|
96
62
|
### Ethereum/EVM
|
|
97
63
|
|
|
98
64
|
```typescript
|
|
99
|
-
import {
|
|
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
|
|
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
|
|
83
|
+
const result2 = await parseToKmsTransaction(rawTx, NetworkId.ETHEREUM_MAINNET);
|
|
118
84
|
|
|
119
85
|
// Hex-encoded transaction
|
|
120
|
-
const result3 = await
|
|
86
|
+
const result3 = await parseToKmsTransaction("0xf86c...", NetworkId.ETHEREUM_MAINNET);
|
|
121
87
|
```
|
|
122
88
|
|
|
123
89
|
### Bitcoin
|
|
124
90
|
|
|
125
91
|
```typescript
|
|
126
|
-
import {
|
|
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
|
|
99
|
+
const result = await parseToKmsTransaction(bitcoinTx, NetworkId.BITCOIN_MAINNET);
|
|
134
100
|
|
|
135
101
|
// Hex-encoded transaction
|
|
136
|
-
const result2 = await
|
|
102
|
+
const result2 = await parseToKmsTransaction("0x0100000001...", NetworkId.BITCOIN_MAINNET);
|
|
137
103
|
```
|
|
138
104
|
|
|
139
105
|
### Sui
|
|
140
106
|
|
|
141
107
|
```typescript
|
|
142
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
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 {
|
|
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
|
-
|
|
33
|
+
deserializeSolanaTransaction: () => deserializeSolanaTransaction,
|
|
34
34
|
parseSignMessageResponse: () => parseSignMessageResponse,
|
|
35
35
|
parseSolanaKitTransactionToSolanaWeb3js: () => parseSolanaKitTransactionToSolanaWeb3js,
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
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
|
|
86
|
+
rawTransaction,
|
|
74
87
|
blockExplorer: (0, import_constants.getExplorerUrl)(networkId, "transaction", hash)
|
|
75
88
|
};
|
|
76
89
|
} else {
|
|
77
90
|
return {
|
|
78
|
-
rawTransaction
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
208
|
-
if (
|
|
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
|
-
|
|
214
|
-
originalFormat: "
|
|
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
|
|
235
|
+
const hex = serialized.startsWith("0x") ? serialized : "0x" + serialized;
|
|
220
236
|
return {
|
|
221
|
-
|
|
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
|
-
|
|
244
|
+
parsed: hex,
|
|
228
245
|
originalFormat: "bytes"
|
|
229
246
|
};
|
|
230
247
|
}
|
|
231
|
-
if (typeof transaction === "
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
348
|
+
deserializeSolanaTransaction,
|
|
301
349
|
parseSignMessageResponse,
|
|
302
350
|
parseSolanaKitTransactionToSolanaWeb3js,
|
|
303
|
-
|
|
304
|
-
|
|
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
|
|
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 {
|
|
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
|
|
47
|
+
rawTransaction,
|
|
36
48
|
blockExplorer: getExplorerUrl(networkId, "transaction", hash)
|
|
37
49
|
};
|
|
38
50
|
} else {
|
|
39
51
|
return {
|
|
40
|
-
rawTransaction
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
164
|
+
parsed: base64urlEncode(serialized),
|
|
147
165
|
originalFormat: "@solana/web3.js"
|
|
148
166
|
};
|
|
149
167
|
}
|
|
150
168
|
if (transaction instanceof Uint8Array) {
|
|
151
169
|
return {
|
|
152
|
-
|
|
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
|
-
|
|
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
|
|
170
|
-
if (
|
|
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
|
-
|
|
176
|
-
originalFormat: "
|
|
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
|
|
196
|
+
const hex = serialized.startsWith("0x") ? serialized : "0x" + serialized;
|
|
182
197
|
return {
|
|
183
|
-
|
|
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
|
-
|
|
205
|
+
parsed: hex,
|
|
190
206
|
originalFormat: "bytes"
|
|
191
207
|
};
|
|
192
208
|
}
|
|
193
|
-
if (typeof transaction === "
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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(
|
|
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
|
-
|
|
253
|
+
parsed: base64urlEncode(serialized),
|
|
207
254
|
originalFormat: "sui-sdk"
|
|
208
255
|
};
|
|
209
256
|
}
|
|
210
257
|
if (transaction instanceof Uint8Array) {
|
|
211
258
|
return {
|
|
212
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
308
|
+
deserializeSolanaTransaction,
|
|
262
309
|
parseSignMessageResponse,
|
|
263
310
|
parseSolanaKitTransactionToSolanaWeb3js,
|
|
264
|
-
|
|
265
|
-
|
|
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.
|
|
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.
|
|
33
|
-
"@phantom/constants": "^1.0.0-beta.
|
|
34
|
-
"@
|
|
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",
|