@shapeshiftoss/hdwallet-phantom 1.55.10-mipdalpha.0 → 1.55.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter.d.ts +2 -2
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +5 -4
- package/dist/adapter.js.map +1 -1
- package/dist/phantom.d.ts +16 -5
- package/dist/phantom.d.ts.map +1 -1
- package/dist/phantom.js +49 -9
- package/dist/phantom.js.map +1 -1
- package/dist/solana.d.ts +9 -0
- package/dist/solana.d.ts.map +1 -0
- package/dist/solana.js +63 -0
- package/dist/solana.js.map +1 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/adapter.ts +5 -6
- package/src/phantom.test.ts +149 -126
- package/src/phantom.ts +68 -12
- package/src/solana.ts +80 -0
- package/src/types.ts +9 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/phantom.test.ts
CHANGED
|
@@ -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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
146
|
+
request: jest.fn().mockRejectedValue(new Error("An Error has occurred")),
|
|
147
|
+
};
|
|
98
148
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
104
|
-
|
|
155
|
+
expect(sig).toBe(null);
|
|
156
|
+
});
|
|
105
157
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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> {
|
|
@@ -147,13 +154,32 @@ export class PhantomHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInf
|
|
|
147
154
|
return btc.btcGetAccountPaths(msg);
|
|
148
155
|
}
|
|
149
156
|
|
|
157
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
158
|
+
public btcIsSameAccount(msg: core.BTCAccountPath[]): boolean {
|
|
159
|
+
throw new Error("Method not implemented.");
|
|
160
|
+
}
|
|
161
|
+
|
|
150
162
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
151
163
|
public btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined {
|
|
152
164
|
throw new Error("Method not implemented");
|
|
153
165
|
}
|
|
166
|
+
|
|
167
|
+
/** Solana */
|
|
168
|
+
|
|
169
|
+
public solanaGetAccountPaths(msg: core.SolanaGetAccountPaths): Array<core.SolanaAccountPath> {
|
|
170
|
+
return core.solanaGetAccountPaths(msg);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
174
|
+
public solanaNextAccountPath(msg: core.SolanaAccountPath): core.SolanaAccountPath | undefined {
|
|
175
|
+
throw new Error("Method not implemented");
|
|
176
|
+
}
|
|
154
177
|
}
|
|
155
178
|
|
|
156
|
-
export class PhantomHDWallet
|
|
179
|
+
export class PhantomHDWallet
|
|
180
|
+
extends PhantomHDWalletInfo
|
|
181
|
+
implements core.HDWallet, core.BTCWallet, core.ETHWallet, core.SolanaWallet
|
|
182
|
+
{
|
|
157
183
|
readonly _supportsBTC = true;
|
|
158
184
|
readonly _supportsETH = true;
|
|
159
185
|
readonly _supportsEthSwitchChain = false;
|
|
@@ -167,20 +193,29 @@ export class PhantomHDWallet extends PhantomHDWalletInfo implements core.HDWalle
|
|
|
167
193
|
readonly _supportsArbitrumNova = false;
|
|
168
194
|
readonly _supportsBase = false;
|
|
169
195
|
readonly _supportsBSC = false;
|
|
196
|
+
readonly _supportsSolana = true;
|
|
170
197
|
readonly _isPhantom = true;
|
|
171
198
|
|
|
172
199
|
evmProvider: PhantomEvmProvider;
|
|
173
200
|
bitcoinProvider: PhantomUtxoProvider;
|
|
201
|
+
solanaProvider: PhantomSolanaProvider;
|
|
202
|
+
|
|
174
203
|
ethAddress?: string | null;
|
|
175
204
|
|
|
176
|
-
constructor(
|
|
205
|
+
constructor(
|
|
206
|
+
evmProvider: PhantomEvmProvider,
|
|
207
|
+
bitcoinProvider: PhantomUtxoProvider,
|
|
208
|
+
solanaProvider: PhantomSolanaProvider
|
|
209
|
+
) {
|
|
177
210
|
super(evmProvider);
|
|
211
|
+
|
|
178
212
|
this.evmProvider = evmProvider;
|
|
179
213
|
this.bitcoinProvider = bitcoinProvider;
|
|
214
|
+
this.solanaProvider = solanaProvider;
|
|
180
215
|
}
|
|
181
216
|
|
|
182
217
|
public async getDeviceID(): Promise<string> {
|
|
183
|
-
return "phantom:" + (await this.ethGetAddress());
|
|
218
|
+
return "phantom:" + (await this.ethGetAddress({ addressNList: [] }));
|
|
184
219
|
}
|
|
185
220
|
|
|
186
221
|
async getFeatures(): Promise<Record<string, any>> {
|
|
@@ -262,11 +297,14 @@ export class PhantomHDWallet extends PhantomHDWalletInfo implements core.HDWalle
|
|
|
262
297
|
|
|
263
298
|
/** Ethereum */
|
|
264
299
|
|
|
265
|
-
|
|
300
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
301
|
+
public async ethGetAddress(_msg: core.ETHGetAddress): Promise<string | null> {
|
|
266
302
|
if (this.ethAddress) {
|
|
267
303
|
return this.ethAddress;
|
|
268
304
|
}
|
|
305
|
+
|
|
269
306
|
const address = await eth.ethGetAddress(this.evmProvider);
|
|
307
|
+
|
|
270
308
|
if (address) {
|
|
271
309
|
this.ethAddress = address;
|
|
272
310
|
return address;
|
|
@@ -278,21 +316,22 @@ export class PhantomHDWallet extends PhantomHDWalletInfo implements core.HDWalle
|
|
|
278
316
|
|
|
279
317
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
280
318
|
public async ethSignTx(msg: core.ETHSignTx): Promise<core.ETHSignedTx | null> {
|
|
281
|
-
|
|
319
|
+
console.error("Method not implemented");
|
|
320
|
+
return null;
|
|
282
321
|
}
|
|
283
322
|
|
|
284
323
|
public async ethSendTx(msg: core.ETHSignTx): Promise<core.ETHTxHash | null> {
|
|
285
|
-
const address = await this.ethGetAddress();
|
|
324
|
+
const address = await this.ethGetAddress({ addressNList: [] });
|
|
286
325
|
return address ? eth.ethSendTx(msg, this.evmProvider, address) : null;
|
|
287
326
|
}
|
|
288
327
|
|
|
289
328
|
public async ethSignMessage(msg: core.ETHSignMessage): Promise<core.ETHSignedMessage | null> {
|
|
290
|
-
const address = await this.ethGetAddress();
|
|
329
|
+
const address = await this.ethGetAddress({ addressNList: [] });
|
|
291
330
|
return address ? eth.ethSignMessage(msg, this.evmProvider, address) : null;
|
|
292
331
|
}
|
|
293
332
|
|
|
294
333
|
async ethSignTypedData(msg: core.ETHSignTypedData): Promise<core.ETHSignedTypedData | null> {
|
|
295
|
-
const address = await this.ethGetAddress();
|
|
334
|
+
const address = await this.ethGetAddress({ addressNList: [] });
|
|
296
335
|
return address ? eth.ethSignTypedData(msg, this.evmProvider, address) : null;
|
|
297
336
|
}
|
|
298
337
|
|
|
@@ -352,4 +391,21 @@ export class PhantomHDWallet extends PhantomHDWalletInfo implements core.HDWalle
|
|
|
352
391
|
const signature = Base64.fromByteArray(core.fromHexString(msg.signature));
|
|
353
392
|
return bitcoinMsg.verify(msg.message, msg.address, signature);
|
|
354
393
|
}
|
|
394
|
+
|
|
395
|
+
/** Solana */
|
|
396
|
+
|
|
397
|
+
public async solanaGetAddress(): Promise<string | null> {
|
|
398
|
+
const { publicKey } = await this.solanaProvider.connect();
|
|
399
|
+
return publicKey.toString();
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
public async solanaSignTx(msg: core.SolanaSignTx): Promise<core.SolanaSignedTx | null> {
|
|
403
|
+
const address = await this.solanaGetAddress();
|
|
404
|
+
return address ? solanaSignTx(msg, this.solanaProvider, address) : null;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
public async solanaSendTx(msg: core.SolanaSignTx): Promise<core.SolanaTxSignature | null> {
|
|
408
|
+
const address = await this.solanaGetAddress();
|
|
409
|
+
return address ? solanaSendTx(msg, this.solanaProvider, address) : null;
|
|
410
|
+
}
|
|
355
411
|
}
|
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
|
+
};
|