@shapeshiftoss/hdwallet-phantom 1.62.29 → 1.62.30

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/src/phantom.ts CHANGED
@@ -8,18 +8,20 @@ import isObject from "lodash/isObject";
8
8
  import * as btc from "./bitcoin";
9
9
  import * as eth from "./ethereum";
10
10
  import { solanaSendTx, solanaSignTx } from "./solana";
11
- import { PhantomEvmProvider, PhantomSolanaProvider, PhantomUtxoProvider } from "./types";
11
+ import * as sui from "./sui";
12
+ import { PhantomEvmProvider, PhantomSolanaProvider, PhantomSuiProvider, PhantomUtxoProvider } from "./types";
12
13
 
13
14
  export function isPhantom(wallet: core.HDWallet): wallet is PhantomHDWallet {
14
15
  return isObject(wallet) && (wallet as any)._isPhantom;
15
16
  }
16
17
 
17
18
  export class PhantomHDWalletInfo
18
- implements core.HDWalletInfo, core.BTCWalletInfo, core.ETHWalletInfo, core.SolanaWalletInfo
19
+ implements core.HDWalletInfo, core.BTCWalletInfo, core.ETHWalletInfo, core.SolanaWalletInfo, core.SuiWalletInfo
19
20
  {
20
21
  readonly _supportsBTCInfo = true;
21
22
  readonly _supportsETHInfo = true;
22
23
  readonly _supportsSolanaInfo = true;
24
+ readonly _supportsSuiInfo = true;
23
25
 
24
26
  evmProvider: PhantomEvmProvider;
25
27
 
@@ -169,47 +171,63 @@ export class PhantomHDWalletInfo
169
171
  public solanaNextAccountPath(msg: core.SolanaAccountPath): core.SolanaAccountPath | undefined {
170
172
  throw new Error("Method not implemented");
171
173
  }
174
+
175
+ /** Sui */
176
+
177
+ public suiGetAccountPaths(msg: core.SuiGetAccountPaths): Array<core.SuiAccountPath> {
178
+ return core.suiGetAccountPaths(msg);
179
+ }
180
+
181
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
182
+ public suiNextAccountPath(msg: core.SuiAccountPath): core.SuiAccountPath | undefined {
183
+ return undefined;
184
+ }
172
185
  }
173
186
 
174
187
  export class PhantomHDWallet
175
188
  extends PhantomHDWalletInfo
176
- implements core.HDWallet, core.BTCWallet, core.ETHWallet, core.SolanaWallet
189
+ implements core.HDWallet, core.BTCWallet, core.ETHWallet, core.SolanaWallet, core.SuiWallet
177
190
  {
178
191
  readonly _supportsBTC = true;
179
192
  readonly _supportsETH = true;
180
- readonly _supportsEthSwitchChain = false;
193
+ readonly _supportsEthSwitchChain = true;
181
194
  readonly _supportsAvalanche = false;
182
195
  readonly _supportsOptimism = false;
183
- // Polygon is technically supported but is acting up on the Phantom side of things atm
184
- // https://github.com/orgs/phantom/discussions/294
185
- readonly _supportsPolygon = false;
196
+ readonly _supportsPolygon = true;
186
197
  readonly _supportsGnosis = false;
187
198
  readonly _supportsArbitrum = false;
188
199
  readonly _supportsArbitrumNova = false;
189
200
  readonly _supportsBase = true;
190
- readonly _supportsMonad = false;
201
+ readonly _supportsMonad = true;
191
202
  readonly _supportsPlasma = false;
192
- readonly _supportsHyperEvm = false;
203
+ readonly _supportsHyperEvm = true;
193
204
  readonly _supportsBSC = false;
194
205
  readonly _supportsSolana = true;
206
+ readonly _supportsSui = true;
195
207
  readonly _isPhantom = true;
196
208
 
197
209
  evmProvider: PhantomEvmProvider;
198
210
  bitcoinProvider: PhantomUtxoProvider;
199
211
  solanaProvider: PhantomSolanaProvider;
212
+ suiProvider?: PhantomSuiProvider;
200
213
 
201
214
  ethAddress?: Address | null;
215
+ btcAddress?: string | null;
216
+ solanaAddress?: string | null;
217
+ suiAddress?: string | null;
202
218
 
203
219
  constructor(
204
220
  evmProvider: PhantomEvmProvider,
205
221
  bitcoinProvider: PhantomUtxoProvider,
206
- solanaProvider: PhantomSolanaProvider
222
+ solanaProvider: PhantomSolanaProvider,
223
+ suiProvider?: PhantomSuiProvider
207
224
  ) {
208
225
  super(evmProvider);
209
226
 
210
227
  this.evmProvider = evmProvider;
211
228
  this.bitcoinProvider = bitcoinProvider;
212
229
  this.solanaProvider = solanaProvider;
230
+ this.suiProvider = suiProvider;
213
231
  }
214
232
 
215
233
  public async getDeviceID(): Promise<string> {
@@ -297,19 +315,17 @@ export class PhantomHDWallet
297
315
 
298
316
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
299
317
  public async ethGetAddress(_msg: core.ETHGetAddress): Promise<core.Address | null> {
300
- if (this.ethAddress) {
301
- return this.ethAddress;
302
- }
318
+ if (this.ethAddress) return this.ethAddress;
303
319
 
304
320
  const address = await eth.ethGetAddress(this.evmProvider);
305
321
 
306
322
  if (address) {
307
323
  this.ethAddress = address;
308
324
  return address;
309
- } else {
310
- this.ethAddress = null;
311
- return null;
312
325
  }
326
+
327
+ this.ethAddress = null;
328
+ return null;
313
329
  }
314
330
 
315
331
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -339,9 +355,34 @@ export class PhantomHDWallet
339
355
  return recoverAddress(digest, msg.signature) === msg.address;
340
356
  }
341
357
 
358
+ public async ethGetChainId(): Promise<number | null> {
359
+ try {
360
+ const chainIdHex = await this.evmProvider.request({ method: "eth_chainId" });
361
+ return parseInt(chainIdHex, 16);
362
+ } catch (error) {
363
+ console.error("Failed to get chain ID from Phantom:", error);
364
+ return null;
365
+ }
366
+ }
367
+
368
+ public async ethSwitchChain(params: core.AddEthereumChainParameter): Promise<void> {
369
+ const parsedChainId = parseInt(params.chainId, 16);
370
+ const currentChainId = await this.ethGetChainId();
371
+
372
+ if (currentChainId === parsedChainId) return;
373
+
374
+ await this.evmProvider.request({
375
+ method: "wallet_switchEthereumChain",
376
+ params: [{ chainId: params.chainId }],
377
+ });
378
+ }
379
+
342
380
  /** Bitcoin */
343
381
 
344
382
  public async btcGetAddress(msg: core.BTCGetAddress): Promise<string | null> {
383
+ // Use cached address if available to prevent rate limiting
384
+ if (this.btcAddress !== undefined) return this.btcAddress;
385
+
345
386
  const value = await (async () => {
346
387
  switch (msg.coin) {
347
388
  case "Bitcoin": {
@@ -354,8 +395,12 @@ export class PhantomHDWallet
354
395
  return null;
355
396
  }
356
397
  })();
357
- if (!value || typeof value !== "string") return null;
398
+ if (!value || typeof value !== "string") {
399
+ this.btcAddress = null;
400
+ return null;
401
+ }
358
402
 
403
+ this.btcAddress = value;
359
404
  return value;
360
405
  }
361
406
 
@@ -393,8 +438,13 @@ export class PhantomHDWallet
393
438
  /** Solana */
394
439
 
395
440
  public async solanaGetAddress(): Promise<string | null> {
441
+ // Use cached address if available to prevent rate limiting
442
+ if (this.solanaAddress !== undefined) return this.solanaAddress;
443
+
396
444
  const { publicKey } = await this.solanaProvider.connect();
397
- return publicKey.toString();
445
+ const address = publicKey.toString();
446
+ this.solanaAddress = address;
447
+ return address;
398
448
  }
399
449
 
400
450
  public async solanaSignTx(msg: core.SolanaSignTx): Promise<core.SolanaSignedTx | null> {
@@ -406,4 +456,23 @@ export class PhantomHDWallet
406
456
  const address = await this.solanaGetAddress();
407
457
  return address ? solanaSendTx(msg, this.solanaProvider, address) : null;
408
458
  }
459
+
460
+ /** Sui */
461
+
462
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
463
+ public async suiGetAddress(_msg: core.SuiGetAddress): Promise<string | null> {
464
+ if (!this.suiProvider) return null;
465
+
466
+ // Use cached address if available to prevent rate limiting
467
+ if (this.suiAddress !== undefined) return this.suiAddress;
468
+
469
+ const address = await sui.suiGetAddress(this.suiProvider);
470
+ this.suiAddress = address;
471
+ return address;
472
+ }
473
+
474
+ public async suiSignTx(msg: core.SuiSignTx): Promise<core.SuiSignedTx | null> {
475
+ if (!this.suiProvider) return null;
476
+ return sui.suiSignTx(msg, this.suiProvider);
477
+ }
409
478
  }
package/src/sui.ts ADDED
@@ -0,0 +1,44 @@
1
+ import * as core from "@shapeshiftoss/hdwallet-core";
2
+
3
+ import { PhantomSuiProvider } from "./types";
4
+
5
+ export async function suiGetAddress(provider: PhantomSuiProvider): Promise<string | null> {
6
+ const account = await provider.requestAccount();
7
+
8
+ if (account && account.address) return account.address;
9
+
10
+ return null;
11
+ }
12
+
13
+ export async function suiSignTx(msg: core.SuiSignTx, provider: PhantomSuiProvider): Promise<core.SuiSignedTx | null> {
14
+ const account = await provider.requestAccount();
15
+
16
+ const result = await provider.signTransaction({
17
+ transaction: msg.transactionJson,
18
+ address: account.address,
19
+ networkID: "sui:mainnet",
20
+ });
21
+
22
+ const fullSignatureBuffer = Buffer.from(result.signature, "base64");
23
+
24
+ // Phantom returns a 97-byte signature: 1 byte flag + 64 bytes signature + 32 bytes public key
25
+ if (fullSignatureBuffer.length !== 97) {
26
+ throw new Error(`Unexpected signature length: ${fullSignatureBuffer.length} bytes (expected 97)`);
27
+ }
28
+
29
+ const signatureBytes = fullSignatureBuffer.slice(1, 65);
30
+ const publicKeyBytes = fullSignatureBuffer.slice(65, 97);
31
+
32
+ const signatureHex = signatureBytes.toString("hex");
33
+ const publicKeyHex = publicKeyBytes.toString("hex");
34
+
35
+ return {
36
+ signature: signatureHex,
37
+ publicKey: publicKeyHex,
38
+ };
39
+ }
40
+
41
+ export async function suiSignMessage(message: Uint8Array, provider: PhantomSuiProvider): Promise<string | null> {
42
+ const result = await provider.signMessage(message);
43
+ return result.signature;
44
+ }
package/src/types.ts CHANGED
@@ -8,6 +8,7 @@ export type PhantomEvmProvider = providers.ExternalProvider & {
8
8
  _metamask: {
9
9
  isUnlocked: () => boolean;
10
10
  };
11
+ request: (args: { method: string; params?: unknown[] | object }) => Promise<unknown>;
11
12
  };
12
13
 
13
14
  export type PhantomUtxoProvider = providers.ExternalProvider & {
@@ -30,3 +31,9 @@ export type PhantomSolanaProvider = providers.ExternalProvider & {
30
31
  signTransaction(transaction: VersionedTransaction): Promise<VersionedTransaction>;
31
32
  signAndSendTransaction(transaction: VersionedTransaction): Promise<{ signature: TransactionSignature }>;
32
33
  };
34
+
35
+ export type PhantomSuiProvider = {
36
+ requestAccount(): Promise<{ address: string; publicKey: Uint8Array }>;
37
+ signMessage(message: Uint8Array, encoding?: string): Promise<{ signature: string }>;
38
+ signTransaction(params: { transaction: string; address: string; networkID: string }): Promise<{ signature: string }>;
39
+ };