@shapeshiftoss/hdwallet-phantom 1.55.10-mipdalpha.1 → 1.55.11-mipd.1

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.
@@ -1,13 +1,14 @@
1
1
  import * as core from "@shapeshiftoss/hdwallet-core";
2
2
 
3
3
  import { PhantomHDWallet } from ".";
4
- import { PhantomUtxoProvider } from "./types";
4
+ import { PhantomSolanaProvider, PhantomUtxoProvider } from "./types";
5
5
 
6
6
  describe("PhantomHDWallet", () => {
7
7
  let wallet: PhantomHDWallet;
8
8
 
9
9
  beforeEach(() => {
10
10
  wallet = new PhantomHDWallet(
11
+ core.untouchable("PhantomHDWallet:provider"),
11
12
  core.untouchable("PhantomHDWallet:provider"),
12
13
  core.untouchable("PhantomHDWallet:provider")
13
14
  );
@@ -28,25 +29,105 @@ describe("PhantomHDWallet", () => {
28
29
  expect(wallet.supportsBroadcast()).toBe(true);
29
30
  });
30
31
 
31
- it("should test ethSignMessage", async () => {
32
- wallet.evmProvider = {
33
- _metamask: {
34
- isUnlocked: () => true,
35
- },
36
- request: jest.fn().mockReturnValue(
37
- `Object {
32
+ describe("Ethereum", () => {
33
+ it("ethGetAddress returns a valid address", async () => {
34
+ wallet.evmProvider = {
35
+ _metamask: {
36
+ isUnlocked: () => true,
37
+ },
38
+ request: jest.fn().mockReturnValue(["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"]),
39
+ };
40
+
41
+ const address = await wallet.ethGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0") });
42
+
43
+ expect(address).toEqual("0x73d0385F4d8E00C5e6504C6030F47BF6212736A8");
44
+ });
45
+
46
+ it("ethSendTx returns a valid hash", async () => {
47
+ wallet.evmProvider = {
48
+ _metamask: {
49
+ isUnlocked: () => true,
50
+ },
51
+ request: jest.fn().mockReturnValue("0x123"),
52
+ };
53
+
54
+ const hash = await wallet.ethSendTx({
55
+ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
56
+ nonce: "0xDEADBEEF",
57
+ gasPrice: "0xDEADBEEF",
58
+ gasLimit: "0xDEADBEEF",
59
+ to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
60
+ value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
61
+ data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
62
+ chainId: 1,
63
+ });
64
+ expect(wallet.evmProvider.request).toHaveBeenCalled();
65
+ expect(hash).toMatchObject({ hash: "0x123" });
66
+ });
67
+
68
+ it("ethSendTx returns a valid hash if maxFeePerGas is present in msg", async () => {
69
+ wallet.evmProvider = {
70
+ _metamask: {
71
+ isUnlocked: () => true,
72
+ },
73
+ request: jest.fn().mockReturnValue("0x123"),
74
+ };
75
+
76
+ const hash = await wallet.ethSendTx({
77
+ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
78
+ nonce: "0xDEADBEEF",
79
+ gasLimit: "0xDEADBEEF",
80
+ maxFeePerGas: "0xDEADBEEF",
81
+ to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
82
+ value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
83
+ data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
84
+ chainId: 1,
85
+ });
86
+ expect(wallet.evmProvider.request).toHaveBeenCalled();
87
+ expect(hash).toMatchObject({ hash: "0x123" });
88
+ });
89
+
90
+ it("ethSendTx returns null on error", async () => {
91
+ wallet.evmProvider = {
92
+ _metamask: {
93
+ isUnlocked: () => true,
94
+ },
95
+ request: jest.fn().mockRejectedValue(new Error("An Error has occurred")),
96
+ };
97
+
98
+ const hash = await wallet.ethSendTx({
99
+ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
100
+ nonce: "0xDEADBEEF",
101
+ gasPrice: "0xDEADBEEF",
102
+ gasLimit: "0xDEADBEEF",
103
+ to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
104
+ value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
105
+ data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
106
+ chainId: 1,
107
+ });
108
+ expect(wallet.evmProvider.request).toHaveBeenCalled();
109
+ expect(hash).toBe(null);
110
+ });
111
+
112
+ it("should test ethSignMessage", async () => {
113
+ wallet.evmProvider = {
114
+ _metamask: {
115
+ isUnlocked: () => true,
116
+ },
117
+ request: jest.fn().mockReturnValue(
118
+ `Object {
38
119
  "address": "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8",
39
120
  "signature": "0x05f51140905ffa33ffdc57f46b0b8d8fbb1d2a99f8cd843ca27893c01c31351c08b76d83dce412731c846e3b50649724415deb522d00950fbf4f2c1459c2b70b1b",
40
121
  }`
41
- ),
42
- };
43
- const msg = "0x737570657220736563726574206d657373616765"; // super secret message
44
- expect(
45
- await wallet.ethSignMessage({
46
- addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
47
- message: msg,
48
- })
49
- ).toMatchInlineSnapshot(`
122
+ ),
123
+ };
124
+ const msg = "0x737570657220736563726574206d657373616765"; // super secret message
125
+ expect(
126
+ await wallet.ethSignMessage({
127
+ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
128
+ message: msg,
129
+ })
130
+ ).toMatchInlineSnapshot(`
50
131
  Object {
51
132
  "address": "O",
52
133
  "signature": "Object {
@@ -55,125 +136,67 @@ describe("PhantomHDWallet", () => {
55
136
  }",
56
137
  }
57
138
  `);
58
- });
59
-
60
- it("ethSignMessage returns null on error", async () => {
61
- wallet.evmProvider = {
62
- _metamask: {
63
- isUnlocked: () => true,
64
- },
65
- request: jest.fn().mockRejectedValue(new Error("An Error has occurred")),
66
- };
67
-
68
- const msg = "0x737570657220736563726574206d657373616765"; // super secret message
69
- const sig = await wallet.ethSignMessage({
70
- addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
71
- message: msg,
72
139
  });
73
140
 
74
- expect(sig).toBe(null);
75
- });
76
-
77
- it("ethGetAddress returns a valid address", async () => {
78
- wallet.evmProvider = {
79
- _metamask: {
80
- isUnlocked: () => true,
81
- },
82
- request: jest.fn().mockReturnValue(["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"]),
83
- };
84
-
85
- const address = await wallet.ethGetAddress();
86
-
87
- expect(address).toEqual("0x73d0385F4d8E00C5e6504C6030F47BF6212736A8");
88
- });
89
- it("btcGetAddress returns a valid address", async () => {
90
- wallet.bitcoinProvider = {
91
- requestAccounts: jest.fn().mockReturnValue([
92
- {
93
- purpose: "payment",
94
- address: "bc1q9sjm947kn2hz84syykmem7dshvevm8xm5dkrpg",
141
+ it("ethSignMessage returns null on error", async () => {
142
+ wallet.evmProvider = {
143
+ _metamask: {
144
+ isUnlocked: () => true,
95
145
  },
96
- ]),
97
- } as unknown as PhantomUtxoProvider;
146
+ request: jest.fn().mockRejectedValue(new Error("An Error has occurred")),
147
+ };
98
148
 
99
- const address = await wallet.btcGetAddress({
100
- coin: "Bitcoin",
101
- } as core.BTCGetAddress);
149
+ const msg = "0x737570657220736563726574206d657373616765"; // super secret message
150
+ const sig = await wallet.ethSignMessage({
151
+ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
152
+ message: msg,
153
+ });
102
154
 
103
- expect(address).toEqual("bc1q9sjm947kn2hz84syykmem7dshvevm8xm5dkrpg");
104
- });
155
+ expect(sig).toBe(null);
156
+ });
105
157
 
106
- it("ethSendTx returns a valid hash", async () => {
107
- wallet.evmProvider = {
108
- _metamask: {
109
- isUnlocked: () => true,
110
- },
111
- request: jest.fn().mockReturnValue("0x123"),
112
- };
113
-
114
- const hash = await wallet.ethSendTx({
115
- addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
116
- nonce: "0xDEADBEEF",
117
- gasPrice: "0xDEADBEEF",
118
- gasLimit: "0xDEADBEEF",
119
- to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
120
- value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
121
- data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
122
- chainId: 1,
158
+ it("ethVerifyMessage returns true for a valid signature", async () => {
159
+ expect(
160
+ await wallet.ethVerifyMessage({
161
+ address: "0x2068dD92B6690255553141Dfcf00dF308281f763",
162
+ message: "Hello World",
163
+ signature:
164
+ "0x61f1dda82e9c3800e960894396c9ce8164fd1526fccb136c71b88442405f7d09721725629915d10bc7cecfca2818fe76bc5816ed96a1b0cebee9b03b052980131b",
165
+ })
166
+ ).toEqual(true);
123
167
  });
124
- expect(wallet.evmProvider.request).toHaveBeenCalled();
125
- expect(hash).toMatchObject({ hash: "0x123" });
126
168
  });
127
- it("ethSendTx returns a valid hash if maxFeePerGas is present in msg", async () => {
128
- wallet.evmProvider = {
129
- _metamask: {
130
- isUnlocked: () => true,
131
- },
132
- request: jest.fn().mockReturnValue("0x123"),
133
- };
134
-
135
- const hash = await wallet.ethSendTx({
136
- addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
137
- nonce: "0xDEADBEEF",
138
- gasLimit: "0xDEADBEEF",
139
- maxFeePerGas: "0xDEADBEEF",
140
- to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
141
- value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
142
- data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
143
- chainId: 1,
169
+
170
+ describe("Bitcoin", () => {
171
+ it("btcGetAddress returns a valid address", async () => {
172
+ wallet.bitcoinProvider = {
173
+ requestAccounts: jest.fn().mockReturnValue([
174
+ {
175
+ purpose: "payment",
176
+ address: "bc1q9sjm947kn2hz84syykmem7dshvevm8xm5dkrpg",
177
+ },
178
+ ]),
179
+ } as unknown as PhantomUtxoProvider;
180
+
181
+ const address = await wallet.btcGetAddress({
182
+ coin: "Bitcoin",
183
+ } as core.BTCGetAddress);
184
+
185
+ expect(address).toEqual("bc1q9sjm947kn2hz84syykmem7dshvevm8xm5dkrpg");
144
186
  });
145
- expect(wallet.evmProvider.request).toHaveBeenCalled();
146
- expect(hash).toMatchObject({ hash: "0x123" });
147
187
  });
148
- it("ethSendTx returns null on error", async () => {
149
- wallet.evmProvider = {
150
- _metamask: {
151
- isUnlocked: () => true,
152
- },
153
- request: jest.fn().mockRejectedValue(new Error("An Error has occurred")),
154
- };
155
-
156
- const hash = await wallet.ethSendTx({
157
- addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
158
- nonce: "0xDEADBEEF",
159
- gasPrice: "0xDEADBEEF",
160
- gasLimit: "0xDEADBEEF",
161
- to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
162
- value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
163
- data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
164
- chainId: 1,
188
+
189
+ describe("Solana", () => {
190
+ it("solanaGetAddress returns a valid address", async () => {
191
+ wallet.solanaProvider = {
192
+ connect: jest.fn().mockReturnValue({
193
+ publicKey: "DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV",
194
+ }),
195
+ } as unknown as PhantomSolanaProvider;
196
+
197
+ const address = await wallet.solanaGetAddress();
198
+
199
+ expect(address).toEqual("DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV");
165
200
  });
166
- expect(wallet.evmProvider.request).toHaveBeenCalled();
167
- expect(hash).toBe(null);
168
- });
169
- it("ethVerifyMessage returns true for a valid signature", async () => {
170
- expect(
171
- await wallet.ethVerifyMessage({
172
- address: "0x2068dD92B6690255553141Dfcf00dF308281f763",
173
- message: "Hello World",
174
- signature:
175
- "0x61f1dda82e9c3800e960894396c9ce8164fd1526fccb136c71b88442405f7d09721725629915d10bc7cecfca2818fe76bc5816ed96a1b0cebee9b03b052980131b",
176
- })
177
- ).toEqual(true);
178
201
  });
179
202
  });
package/src/phantom.ts CHANGED
@@ -7,15 +7,19 @@ import _ from "lodash";
7
7
 
8
8
  import * as btc from "./bitcoin";
9
9
  import * as eth from "./ethereum";
10
- import { PhantomEvmProvider, PhantomUtxoProvider } from "./types";
10
+ import { solanaSendTx, solanaSignTx } from "./solana";
11
+ import { PhantomEvmProvider, PhantomSolanaProvider, PhantomUtxoProvider } from "./types";
11
12
 
12
13
  export function isPhantom(wallet: core.HDWallet): wallet is PhantomHDWallet {
13
14
  return _.isObject(wallet) && (wallet as any)._isPhantom;
14
15
  }
15
16
 
16
- export class PhantomHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInfo, core.ETHWalletInfo {
17
+ export class PhantomHDWalletInfo
18
+ implements core.HDWalletInfo, core.BTCWalletInfo, core.ETHWalletInfo, core.SolanaWalletInfo
19
+ {
17
20
  readonly _supportsBTCInfo = true;
18
21
  readonly _supportsETHInfo = true;
22
+ readonly _supportsSolanaInfo = true;
19
23
 
20
24
  evmProvider: PhantomEvmProvider;
21
25
 
@@ -73,6 +77,8 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInf
73
77
  }
74
78
  case "ethereum":
75
79
  return core.describeETHPath(msg.path);
80
+ case "solana":
81
+ return core.solanaDescribePath(msg.path);
76
82
  default:
77
83
  throw new Error("Unsupported path");
78
84
  }
@@ -114,13 +120,14 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInf
114
120
 
115
121
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
116
122
  public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined {
117
- throw new Error("Method not implemented");
123
+ console.error("Method not implemented");
124
+ return undefined;
118
125
  }
119
126
 
120
127
  /** Bitcoin */
121
128
 
122
129
  public async btcSupportsCoin(coin: core.Coin): Promise<boolean> {
123
- return coin === "bitcoin";
130
+ return coin.toLowerCase() === "bitcoin";
124
131
  }
125
132
 
126
133
  public async btcSupportsScriptType(coin: string, scriptType?: core.BTCInputScriptType | undefined): Promise<boolean> {
@@ -151,9 +158,23 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInf
151
158
  public btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined {
152
159
  throw new Error("Method not implemented");
153
160
  }
161
+
162
+ /** Solana */
163
+
164
+ public solanaGetAccountPaths(msg: core.SolanaGetAccountPaths): Array<core.SolanaAccountPath> {
165
+ return core.solanaGetAccountPaths(msg);
166
+ }
167
+
168
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
169
+ public solanaNextAccountPath(msg: core.SolanaAccountPath): core.SolanaAccountPath | undefined {
170
+ throw new Error("Method not implemented");
171
+ }
154
172
  }
155
173
 
156
- export class PhantomHDWallet extends PhantomHDWalletInfo implements core.HDWallet, core.BTCWallet, core.ETHWallet {
174
+ export class PhantomHDWallet
175
+ extends PhantomHDWalletInfo
176
+ implements core.HDWallet, core.BTCWallet, core.ETHWallet, core.SolanaWallet
177
+ {
157
178
  readonly _supportsBTC = true;
158
179
  readonly _supportsETH = true;
159
180
  readonly _supportsEthSwitchChain = false;
@@ -167,20 +188,29 @@ export class PhantomHDWallet extends PhantomHDWalletInfo implements core.HDWalle
167
188
  readonly _supportsArbitrumNova = false;
168
189
  readonly _supportsBase = false;
169
190
  readonly _supportsBSC = false;
191
+ readonly _supportsSolana = true;
170
192
  readonly _isPhantom = true;
171
193
 
172
194
  evmProvider: PhantomEvmProvider;
173
195
  bitcoinProvider: PhantomUtxoProvider;
196
+ solanaProvider: PhantomSolanaProvider;
197
+
174
198
  ethAddress?: string | null;
175
199
 
176
- constructor(evmProvider: PhantomEvmProvider, bitcoinProvider: PhantomUtxoProvider) {
200
+ constructor(
201
+ evmProvider: PhantomEvmProvider,
202
+ bitcoinProvider: PhantomUtxoProvider,
203
+ solanaProvider: PhantomSolanaProvider
204
+ ) {
177
205
  super(evmProvider);
206
+
178
207
  this.evmProvider = evmProvider;
179
208
  this.bitcoinProvider = bitcoinProvider;
209
+ this.solanaProvider = solanaProvider;
180
210
  }
181
211
 
182
212
  public async getDeviceID(): Promise<string> {
183
- return "phantom:" + (await this.ethGetAddress());
213
+ return "phantom:" + (await this.ethGetAddress({ addressNList: [] }));
184
214
  }
185
215
 
186
216
  async getFeatures(): Promise<Record<string, any>> {
@@ -262,11 +292,14 @@ export class PhantomHDWallet extends PhantomHDWalletInfo implements core.HDWalle
262
292
 
263
293
  /** Ethereum */
264
294
 
265
- public async ethGetAddress(): Promise<string | null> {
295
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
296
+ public async ethGetAddress(_msg: core.ETHGetAddress): Promise<string | null> {
266
297
  if (this.ethAddress) {
267
298
  return this.ethAddress;
268
299
  }
300
+
269
301
  const address = await eth.ethGetAddress(this.evmProvider);
302
+
270
303
  if (address) {
271
304
  this.ethAddress = address;
272
305
  return address;
@@ -278,21 +311,22 @@ export class PhantomHDWallet extends PhantomHDWalletInfo implements core.HDWalle
278
311
 
279
312
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
280
313
  public async ethSignTx(msg: core.ETHSignTx): Promise<core.ETHSignedTx | null> {
281
- throw new Error("Method not implemented");
314
+ console.error("Method not implemented");
315
+ return null;
282
316
  }
283
317
 
284
318
  public async ethSendTx(msg: core.ETHSignTx): Promise<core.ETHTxHash | null> {
285
- const address = await this.ethGetAddress();
319
+ const address = await this.ethGetAddress({ addressNList: [] });
286
320
  return address ? eth.ethSendTx(msg, this.evmProvider, address) : null;
287
321
  }
288
322
 
289
323
  public async ethSignMessage(msg: core.ETHSignMessage): Promise<core.ETHSignedMessage | null> {
290
- const address = await this.ethGetAddress();
324
+ const address = await this.ethGetAddress({ addressNList: [] });
291
325
  return address ? eth.ethSignMessage(msg, this.evmProvider, address) : null;
292
326
  }
293
327
 
294
328
  async ethSignTypedData(msg: core.ETHSignTypedData): Promise<core.ETHSignedTypedData | null> {
295
- const address = await this.ethGetAddress();
329
+ const address = await this.ethGetAddress({ addressNList: [] });
296
330
  return address ? eth.ethSignTypedData(msg, this.evmProvider, address) : null;
297
331
  }
298
332
 
@@ -352,4 +386,21 @@ export class PhantomHDWallet extends PhantomHDWalletInfo implements core.HDWalle
352
386
  const signature = Base64.fromByteArray(core.fromHexString(msg.signature));
353
387
  return bitcoinMsg.verify(msg.message, msg.address, signature);
354
388
  }
389
+
390
+ /** Solana */
391
+
392
+ public async solanaGetAddress(): Promise<string | null> {
393
+ const { publicKey } = await this.solanaProvider.connect();
394
+ return publicKey.toString();
395
+ }
396
+
397
+ public async solanaSignTx(msg: core.SolanaSignTx): Promise<core.SolanaSignedTx | null> {
398
+ const address = await this.solanaGetAddress();
399
+ return address ? solanaSignTx(msg, this.solanaProvider, address) : null;
400
+ }
401
+
402
+ public async solanaSendTx(msg: core.SolanaSignTx): Promise<core.SolanaTxSignature | null> {
403
+ const address = await this.solanaGetAddress();
404
+ return address ? solanaSendTx(msg, this.solanaProvider, address) : null;
405
+ }
355
406
  }
package/src/solana.ts ADDED
@@ -0,0 +1,80 @@
1
+ import * as core from "@shapeshiftoss/hdwallet-core";
2
+ import {
3
+ ComputeBudgetProgram,
4
+ PublicKey,
5
+ SystemProgram,
6
+ TransactionInstruction,
7
+ TransactionMessage,
8
+ VersionedTransaction,
9
+ } from "@solana/web3.js";
10
+
11
+ import { PhantomSolanaProvider } from "./types";
12
+
13
+ export type SolanaAccount = {
14
+ publicKey: PublicKey;
15
+ };
16
+
17
+ function toTransactionInstructions(instructions: core.SolanaTxInstruction[]): TransactionInstruction[] {
18
+ return instructions.map(
19
+ (instruction) =>
20
+ new TransactionInstruction({
21
+ keys: instruction.keys.map((key) => Object.assign(key, { pubkey: new PublicKey(key.pubkey) })),
22
+ programId: new PublicKey(instruction.programId),
23
+ data: instruction.data,
24
+ })
25
+ );
26
+ }
27
+
28
+ function buildTransaction(msg: core.SolanaSignTx, address: string): VersionedTransaction {
29
+ const instructions = toTransactionInstructions(msg.instructions ?? []);
30
+
31
+ const value = Number(msg.value);
32
+ if (!isNaN(value) && value > 0 && msg.to) {
33
+ instructions.push(
34
+ SystemProgram.transfer({
35
+ fromPubkey: new PublicKey(address),
36
+ toPubkey: new PublicKey(msg.to),
37
+ lamports: value,
38
+ })
39
+ );
40
+ }
41
+
42
+ if (msg.computeUnitLimit !== undefined) {
43
+ instructions.push(ComputeBudgetProgram.setComputeUnitLimit({ units: msg.computeUnitLimit }));
44
+ }
45
+
46
+ if (msg.computeUnitPrice !== undefined) {
47
+ instructions.push(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: msg.computeUnitPrice }));
48
+ }
49
+
50
+ const message = new TransactionMessage({
51
+ payerKey: new PublicKey(address),
52
+ instructions,
53
+ recentBlockhash: msg.blockHash,
54
+ }).compileToV0Message();
55
+
56
+ return new VersionedTransaction(message);
57
+ }
58
+
59
+ export async function solanaSignTx(
60
+ msg: core.SolanaSignTx,
61
+ provider: PhantomSolanaProvider,
62
+ address: string
63
+ ): Promise<core.SolanaSignedTx | null> {
64
+ const transaction = buildTransaction(msg, address);
65
+ const signedTransaction = await provider.signTransaction(transaction);
66
+ return {
67
+ serialized: Buffer.from(signedTransaction.serialize()).toString("base64"),
68
+ signatures: signedTransaction.signatures.map((signature) => Buffer.from(signature).toString("base64")),
69
+ };
70
+ }
71
+
72
+ export async function solanaSendTx(
73
+ msg: core.SolanaSignTx,
74
+ provider: PhantomSolanaProvider,
75
+ address: string
76
+ ): Promise<core.SolanaTxSignature | null> {
77
+ const transaction = buildTransaction(msg, address);
78
+ const { signature } = await provider.signAndSendTransaction(transaction);
79
+ return { signature };
80
+ }
package/src/types.ts CHANGED
@@ -1,6 +1,8 @@
1
+ import { PublicKey, VersionedTransaction } from "@solana/web3.js";
1
2
  import { providers } from "ethers";
2
3
 
3
4
  import { BtcAccount } from "./bitcoin";
5
+ import { SolanaAccount } from "./solana";
4
6
 
5
7
  export type PhantomEvmProvider = providers.ExternalProvider & {
6
8
  _metamask: {
@@ -21,3 +23,10 @@ export type PhantomUtxoProvider = providers.ExternalProvider & {
21
23
  options: { inputsToSign: { sigHash?: number | undefined; address: string; signingIndexes: number[] }[] }
22
24
  ): Promise<Uint8Array>;
23
25
  };
26
+
27
+ export type PhantomSolanaProvider = providers.ExternalProvider & {
28
+ publicKey?: PublicKey;
29
+ connect(): Promise<SolanaAccount>;
30
+ signTransaction(transaction: VersionedTransaction): Promise<VersionedTransaction>;
31
+ signAndSendTransaction(transaction: VersionedTransaction): Promise<{ signature: any }>;
32
+ };