@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.
- 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 +15 -5
- package/dist/phantom.d.ts.map +1 -1
- package/dist/phantom.js +45 -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 +63 -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> {
|
|
@@ -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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
};
|