@shapeshiftoss/hdwallet-vultisig 1.62.5

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.
Files changed (50) hide show
  1. package/LICENSE.md +21 -0
  2. package/dist/adapter.d.ts +22 -0
  3. package/dist/adapter.d.ts.map +1 -0
  4. package/dist/adapter.js +84 -0
  5. package/dist/adapter.js.map +1 -0
  6. package/dist/bitcoin.d.ts +6 -0
  7. package/dist/bitcoin.d.ts.map +1 -0
  8. package/dist/bitcoin.js +184 -0
  9. package/dist/bitcoin.js.map +1 -0
  10. package/dist/cosmos.d.ts +6 -0
  11. package/dist/cosmos.d.ts.map +1 -0
  12. package/dist/cosmos.js +47 -0
  13. package/dist/cosmos.js.map +1 -0
  14. package/dist/ethereum.d.ts +12 -0
  15. package/dist/ethereum.d.ts.map +1 -0
  16. package/dist/ethereum.js +193 -0
  17. package/dist/ethereum.js.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +19 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/solana.d.ts +9 -0
  23. package/dist/solana.d.ts.map +1 -0
  24. package/dist/solana.js +55 -0
  25. package/dist/solana.js.map +1 -0
  26. package/dist/thorchain.d.ts +6 -0
  27. package/dist/thorchain.d.ts.map +1 -0
  28. package/dist/thorchain.js +46 -0
  29. package/dist/thorchain.js.map +1 -0
  30. package/dist/types.d.ts +50 -0
  31. package/dist/types.d.ts.map +1 -0
  32. package/dist/types.js +3 -0
  33. package/dist/types.js.map +1 -0
  34. package/dist/vultisig.d.ts +123 -0
  35. package/dist/vultisig.d.ts.map +1 -0
  36. package/dist/vultisig.js +482 -0
  37. package/dist/vultisig.js.map +1 -0
  38. package/package.json +31 -0
  39. package/src/adapter.ts +72 -0
  40. package/src/bitcoin.ts +171 -0
  41. package/src/cosmos.ts +43 -0
  42. package/src/ethereum.ts +169 -0
  43. package/src/index.ts +2 -0
  44. package/src/solana.ts +31 -0
  45. package/src/thorchain.ts +43 -0
  46. package/src/types.ts +62 -0
  47. package/src/vultisig.test.ts +253 -0
  48. package/src/vultisig.ts +459 -0
  49. package/tsconfig.json +10 -0
  50. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,253 @@
1
+ import * as core from "@shapeshiftoss/hdwallet-core";
2
+
3
+ import { VultisigHDWallet } from ".";
4
+ import { VultisigSolanaProvider, VultisigUtxoProvider } from "./types";
5
+
6
+ describe("VultisigHDWallet", () => {
7
+ let wallet: VultisigHDWallet;
8
+
9
+ beforeEach(() => {
10
+ wallet = new VultisigHDWallet({
11
+ evmProvider: core.untouchable("VultisigHDWallet:provider"),
12
+ bitcoinProvider: core.untouchable("VultisigHDWallet:provider"),
13
+ solanaProvider: core.untouchable("VultisigHDWallet:provider"),
14
+ thorchainProvider: core.untouchable("VultisigHDWallet:provider"),
15
+ cosmosProvider: core.untouchable("VultisigHDWallet:provider"),
16
+ });
17
+ });
18
+
19
+ it("should match the metadata", async () => {
20
+ expect(wallet.getVendor()).toBe("Vultisig");
21
+ expect(wallet.hasOnDevicePinEntry()).toBe(false);
22
+ expect(wallet.hasOnDevicePassphrase()).toBe(false);
23
+ expect(wallet.hasOnDeviceDisplay()).toBe(true);
24
+ expect(wallet.hasOnDeviceRecovery()).toBe(true);
25
+ expect(await wallet.ethSupportsNetwork(1)).toBe(true);
26
+ expect(await wallet.ethSupportsSecureTransfer()).toBe(false);
27
+ expect(wallet.ethSupportsNativeShapeShift()).toBe(false);
28
+ expect(await wallet.ethSupportsEIP1559()).toBe(true);
29
+ expect(wallet.supportsOfflineSigning()).toBe(false);
30
+ expect(wallet.supportsBip44Accounts()).toBe(false);
31
+ expect(wallet.supportsBroadcast()).toBe(true);
32
+ });
33
+
34
+ describe("Ethereum", () => {
35
+ it("ethGetAddress returns a valid address", async () => {
36
+ wallet.evmProvider = {
37
+ request: jest.fn().mockReturnValue(["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"]),
38
+ };
39
+
40
+ const address = await wallet.ethGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0") });
41
+
42
+ expect(address).toEqual("0x73d0385F4d8E00C5e6504C6030F47BF6212736A8");
43
+ });
44
+
45
+ it("ethSendTx returns a valid hash", async () => {
46
+ wallet.evmProvider = {
47
+ request: jest.fn().mockReturnValue("0x123"),
48
+ };
49
+
50
+ const hash = await wallet.ethSendTx({
51
+ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
52
+ nonce: "0xDEADBEEF",
53
+ gasPrice: "0xDEADBEEF",
54
+ gasLimit: "0xDEADBEEF",
55
+ to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
56
+ value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
57
+ data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
58
+ chainId: 1,
59
+ });
60
+ expect(wallet.evmProvider.request).toHaveBeenCalled();
61
+ expect(hash).toMatchObject({ hash: "0x123" });
62
+ });
63
+
64
+ it("ethSendTx returns a valid hash if maxFeePerGas is present in msg", async () => {
65
+ wallet.evmProvider = {
66
+ request: jest.fn().mockReturnValue("0x123"),
67
+ };
68
+
69
+ const hash = await wallet.ethSendTx({
70
+ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
71
+ nonce: "0xDEADBEEF",
72
+ gasLimit: "0xDEADBEEF",
73
+ maxFeePerGas: "0xDEADBEEF",
74
+ to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
75
+ value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
76
+ data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
77
+ chainId: 1,
78
+ });
79
+ expect(wallet.evmProvider.request).toHaveBeenCalled();
80
+ expect(hash).toMatchObject({ hash: "0x123" });
81
+ });
82
+
83
+ it("ethSendTx returns null on error", async () => {
84
+ wallet.evmProvider = {
85
+ request: jest.fn().mockRejectedValue(new Error("An Error has occurred")),
86
+ };
87
+
88
+ const hash = await wallet.ethSendTx({
89
+ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
90
+ nonce: "0xDEADBEEF",
91
+ gasPrice: "0xDEADBEEF",
92
+ gasLimit: "0xDEADBEEF",
93
+ to: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
94
+ value: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
95
+ data: "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
96
+ chainId: 1,
97
+ });
98
+ expect(wallet.evmProvider.request).toHaveBeenCalled();
99
+ expect(hash).toBe(null);
100
+ });
101
+
102
+ it("should test ethSignMessage", async () => {
103
+ wallet.evmProvider = {
104
+ request: jest
105
+ .fn()
106
+ .mockReturnValue(
107
+ "0x05f51140905ffa33ffdc57f46b0b8d8fbb1d2a99f8cd843ca27893c01c31351c08b76d83dce412731c846e3b50649724415deb522d00950fbf4f2c1459c2b70b1b"
108
+ ),
109
+ };
110
+ const msg = "0x737570657220736563726574206d657373616765"; // super secret message
111
+ expect(
112
+ await wallet.ethSignMessage({
113
+ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
114
+ message: msg,
115
+ })
116
+ ).toMatchInlineSnapshot(`
117
+ Object {
118
+ "address": "0",
119
+ "signature": "0x05f51140905ffa33ffdc57f46b0b8d8fbb1d2a99f8cd843ca27893c01c31351c08b76d83dce412731c846e3b50649724415deb522d00950fbf4f2c1459c2b70b1b",
120
+ }
121
+ `);
122
+ });
123
+
124
+ it("ethSignMessage returns null on error", async () => {
125
+ wallet.evmProvider = {
126
+ request: jest.fn().mockRejectedValue(new Error("An Error has occurred")),
127
+ };
128
+
129
+ const msg = "0x737570657220736563726574206d657373616765"; // super secret message
130
+ const sig = await wallet.ethSignMessage({
131
+ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"),
132
+ message: msg,
133
+ });
134
+
135
+ expect(sig).toBe(null);
136
+ });
137
+
138
+ it("ethVerifyMessage returns true for a valid signature", async () => {
139
+ expect(
140
+ await wallet.ethVerifyMessage({
141
+ address: "0x2068dD92B6690255553141Dfcf00dF308281f763",
142
+ message: "Hello World",
143
+ signature:
144
+ "0x61f1dda82e9c3800e960894396c9ce8164fd1526fccb136c71b88442405f7d09721725629915d10bc7cecfca2818fe76bc5816ed96a1b0cebee9b03b052980131b",
145
+ })
146
+ ).toEqual(true);
147
+ });
148
+ });
149
+
150
+ describe("Bitcoin", () => {
151
+ it("btcGetAddress returns a valid address", async () => {
152
+ wallet.bitcoinProvider = {
153
+ request: jest.fn().mockReturnValue(["bc1q9sjm947kn2hz84syykmem7dshvevm8xm5dkrpg"]),
154
+ } as unknown as VultisigUtxoProvider;
155
+
156
+ const address = await wallet.btcGetAddress({
157
+ coin: "Bitcoin",
158
+ } as core.BTCGetAddress);
159
+
160
+ expect(address).toEqual("bc1q9sjm947kn2hz84syykmem7dshvevm8xm5dkrpg");
161
+ });
162
+
163
+ // TODO: hippo: Add tests for getAddress over non-supported script types
164
+
165
+ describe("btcGetAccountPaths", () => {
166
+ it("should return correct paths for Bitcoin (BIP84)", () => {
167
+ const paths = wallet.btcGetAccountPaths({
168
+ coin: "Bitcoin",
169
+ accountIdx: 0,
170
+ });
171
+
172
+ expect(paths).toHaveLength(1);
173
+ expect(paths[0]).toEqual({
174
+ coin: "Bitcoin",
175
+ scriptType: core.BTCInputScriptType.SpendWitness,
176
+ addressNList: [0x80000000 + 84, 0x80000000 + 0, 0x80000000 + 0],
177
+ });
178
+ });
179
+
180
+ it("should filter paths by scriptType when specified", () => {
181
+ const paths = wallet.btcGetAccountPaths({
182
+ coin: "Bitcoin",
183
+ accountIdx: 0,
184
+ scriptType: core.BTCInputScriptType.SpendWitness,
185
+ });
186
+
187
+ expect(paths).toHaveLength(1);
188
+ expect(paths[0].scriptType).toBe(core.BTCInputScriptType.SpendWitness);
189
+ });
190
+
191
+ it("should return empty array for unsupported coin", () => {
192
+ const paths = wallet.btcGetAccountPaths({
193
+ coin: "UnsupportedCoin" as any,
194
+ accountIdx: 0,
195
+ });
196
+
197
+ expect(paths).toHaveLength(0);
198
+ });
199
+ });
200
+
201
+ describe("btcSupportsScriptType", () => {
202
+ it("should support SpendWitness for Bitcoin", async () => {
203
+ const result = await wallet.btcSupportsScriptType("Bitcoin", core.BTCInputScriptType.SpendWitness);
204
+ expect(result).toBe(true);
205
+ });
206
+
207
+ it("should support SpendAddress for Bitcoin", async () => {
208
+ const result = await wallet.btcSupportsScriptType("Bitcoin", core.BTCInputScriptType.SpendAddress);
209
+ expect(result).toBe(true);
210
+ });
211
+
212
+ it("should NOT support SpendP2SHWitness for any coin", async () => {
213
+ const coins = ["Bitcoin"];
214
+ for (const coin of coins) {
215
+ const result = await wallet.btcSupportsScriptType(coin, core.BTCInputScriptType.SpendP2SHWitness);
216
+ expect(result).toBe(false);
217
+ }
218
+ });
219
+
220
+ it("should NOT support unsupported script types", async () => {
221
+ const coins = ["Bitcoin"];
222
+ for (const coin of coins) {
223
+ const result = await wallet.btcSupportsScriptType(coin, "UnsupportedScriptType" as any);
224
+ expect(result).toBe(false);
225
+ }
226
+ });
227
+
228
+ it("should return false for unsupported coin", async () => {
229
+ const result = await wallet.btcSupportsScriptType(
230
+ "UnsupportedCoin" as any,
231
+ core.BTCInputScriptType.SpendWitness
232
+ );
233
+ expect(result).toBe(false);
234
+ });
235
+ });
236
+ });
237
+
238
+ describe("Solana", () => {
239
+ it("solanaGetAddress returns a valid address", async () => {
240
+ wallet.solanaProvider = {
241
+ connect: jest.fn().mockReturnValue({
242
+ publicKey: "DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV",
243
+ }),
244
+ } as unknown as VultisigSolanaProvider;
245
+
246
+ const address = await wallet.solanaGetAddress();
247
+
248
+ expect(address).toEqual("DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV");
249
+ });
250
+ });
251
+
252
+ // TODO: hippo: Missing tests for Thorchain and Cosmos
253
+ });
@@ -0,0 +1,459 @@
1
+ import * as core from "@shapeshiftoss/hdwallet-core";
2
+ import { Address } from "@shapeshiftoss/hdwallet-core";
3
+ import Base64 from "base64-js";
4
+ import * as bitcoinMsg from "bitcoinjs-message";
5
+ import { keccak256, recoverAddress } from "ethers/lib/utils.js";
6
+ import isObject from "lodash/isObject";
7
+
8
+ import * as btc from "./bitcoin";
9
+ import * as cosmos from "./cosmos";
10
+ import * as eth from "./ethereum";
11
+ import { solanaSendTx, solanaSignTx } from "./solana";
12
+ import * as thorchain from "./thorchain";
13
+ import { VultisigEvmProvider, VultisigOfflineProvider, VultisigSolanaProvider, VultisigUtxoProvider } from "./types";
14
+
15
+ export function isVultisig(wallet: core.HDWallet): wallet is VultisigHDWallet {
16
+ return isObject(wallet) && (wallet as any)._isVultisig;
17
+ }
18
+
19
+ export class VultisigHDWalletInfo
20
+ implements
21
+ core.HDWalletInfo,
22
+ core.BTCWalletInfo,
23
+ core.ETHWalletInfo,
24
+ core.SolanaWalletInfo,
25
+ core.ThorchainWalletInfo,
26
+ core.CosmosWalletInfo
27
+ {
28
+ // TODO(gomes): turn me back once signPSBT is fixed upstream
29
+ readonly _supportsBTCInfo = false;
30
+ readonly _supportsETHInfo = true;
31
+ readonly _supportsSolanaInfo = true;
32
+ readonly _supportsThorchainInfo = true;
33
+ readonly _supportsCosmosInfo = true;
34
+
35
+ constructor() {}
36
+
37
+ public getVendor(): string {
38
+ return "Vultisig";
39
+ }
40
+
41
+ public hasOnDevicePinEntry(): boolean {
42
+ return false;
43
+ }
44
+
45
+ public hasOnDevicePassphrase(): boolean {
46
+ return false;
47
+ }
48
+
49
+ public hasOnDeviceDisplay(): boolean {
50
+ return true;
51
+ }
52
+
53
+ public hasOnDeviceRecovery(): boolean {
54
+ return true;
55
+ }
56
+
57
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
58
+ public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean {
59
+ return false;
60
+ }
61
+
62
+ public supportsBip44Accounts(): boolean {
63
+ return false;
64
+ }
65
+
66
+ public supportsOfflineSigning(): boolean {
67
+ return false;
68
+ }
69
+
70
+ public supportsBroadcast(): boolean {
71
+ return true;
72
+ }
73
+
74
+ public describePath(msg: core.DescribePath): core.PathDescription {
75
+ switch (msg.coin.toLowerCase()) {
76
+ case "bitcoin": {
77
+ // case "zcash": // case "bitcoincash": // case "dogecoin": // case "litecoin":
78
+ const unknown = core.unknownUTXOPath(msg.path, msg.coin, msg.scriptType);
79
+
80
+ if (!msg.scriptType) return unknown;
81
+ if (!this.btcSupportsCoin(msg.coin)) return unknown;
82
+ if (!this.btcSupportsScriptType(msg.coin, msg.scriptType)) return unknown;
83
+
84
+ return core.describeUTXOPath(msg.path, msg.coin, msg.scriptType);
85
+ }
86
+ case "ethereum": {
87
+ return core.describeETHPath(msg.path);
88
+ }
89
+ case "solana":
90
+ return core.solanaDescribePath(msg.path);
91
+ case "cosmos":
92
+ return core.cosmosDescribePath(msg.path);
93
+ case "thorchain":
94
+ return core.thorchainDescribePath(msg.path);
95
+ default:
96
+ throw new Error(`Unsupported path for coin: ${msg.coin}`);
97
+ }
98
+ }
99
+
100
+ /** Ethereum */
101
+
102
+ public async ethSupportsNetwork(chainId = 1): Promise<boolean> {
103
+ return chainId === 1;
104
+ }
105
+
106
+ public async ethSupportsSecureTransfer(): Promise<boolean> {
107
+ return false;
108
+ }
109
+
110
+ public ethSupportsNativeShapeShift(): boolean {
111
+ return false;
112
+ }
113
+
114
+ public async ethSupportsEIP1559(): Promise<boolean> {
115
+ return true;
116
+ }
117
+
118
+ public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array<core.ETHAccountPath> {
119
+ return eth.ethGetAccountPaths(msg);
120
+ }
121
+
122
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
123
+ public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined {
124
+ console.error("Method not implemented");
125
+ return undefined;
126
+ }
127
+
128
+ /** Bitcoin */
129
+
130
+ public async btcSupportsCoin(coin: core.Coin): Promise<boolean> {
131
+ const vultisigSupportedCoins = ["bitcoin"]; // Just BTC for first part of integration, we will enable it progressively
132
+ return vultisigSupportedCoins.includes(coin.toLowerCase());
133
+ }
134
+
135
+ public async btcSupportsScriptType(coin: string, scriptType?: core.BTCInputScriptType | undefined): Promise<boolean> {
136
+ if (!this.btcSupportsCoin(coin)) return false;
137
+
138
+ const c = coin.toLowerCase();
139
+ switch (scriptType) {
140
+ case core.BTCInputScriptType.SpendAddress:
141
+ return ["bitcoin"].includes(c);
142
+ case core.BTCInputScriptType.SpendWitness:
143
+ return ["bitcoin"].includes(c);
144
+ default:
145
+ return false;
146
+ }
147
+ }
148
+
149
+ public async btcSupportsSecureTransfer(): Promise<boolean> {
150
+ return false;
151
+ }
152
+
153
+ public btcSupportsNativeShapeShift(): boolean {
154
+ return false;
155
+ }
156
+
157
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
158
+ public btcGetAccountPaths(msg: core.BTCGetAccountPaths): Array<core.BTCAccountPath> {
159
+ return btc.btcGetAccountPaths(msg);
160
+ }
161
+
162
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
163
+ public btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined {
164
+ throw new Error("Method not implemented");
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
+ }
177
+
178
+ public thorchainGetAccountPaths(msg: core.ThorchainGetAccountPaths): Array<core.ThorchainAccountPath> {
179
+ return thorchain.thorchainGetAccountPaths(msg);
180
+ }
181
+
182
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
183
+ public thorchainNextAccountPath(msg: core.ThorchainAccountPath): core.ThorchainAccountPath | undefined {
184
+ throw new Error("Method not implemented.");
185
+ }
186
+
187
+ public cosmosGetAccountPaths(msg: core.CosmosGetAccountPaths): Array<core.CosmosAccountPath> {
188
+ return cosmos.cosmosGetAccountPaths(msg);
189
+ }
190
+
191
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
192
+ public cosmosNextAccountPath(_msg: core.CosmosAccountPath): core.CosmosAccountPath | undefined {
193
+ throw new Error("Method not implemented.");
194
+ }
195
+ }
196
+
197
+ export class VultisigHDWallet
198
+ extends VultisigHDWalletInfo
199
+ implements core.HDWallet, core.BTCWallet, core.ETHWallet, core.SolanaWallet, core.ThorchainWallet, core.CosmosWallet
200
+ {
201
+ // TODO(gomes): turn me back once signPSBT is fixed upstream
202
+ readonly _supportsBTC = false;
203
+ readonly _supportsETH = true;
204
+ readonly _supportsEthSwitchChain = true;
205
+ readonly _supportsAvalanche = true;
206
+ readonly _supportsOptimism = true;
207
+ readonly _supportsPolygon = true;
208
+ readonly _supportsGnosis = false;
209
+ readonly _supportsArbitrum = true;
210
+ readonly _supportsArbitrumNova = false;
211
+ readonly _supportsBase = true;
212
+ readonly _supportsBSC = true;
213
+ readonly _supportsSolana = true;
214
+ readonly _supportsThorchain = true;
215
+ readonly _supportsCosmos = true;
216
+ readonly _isVultisig = true;
217
+
218
+ evmProvider: VultisigEvmProvider;
219
+ bitcoinProvider: VultisigUtxoProvider;
220
+ solanaProvider: VultisigSolanaProvider;
221
+ thorchainProvider: VultisigOfflineProvider;
222
+ cosmosProvider: VultisigOfflineProvider;
223
+
224
+ ethAddress?: Address | null;
225
+
226
+ constructor(providers: {
227
+ evmProvider: VultisigEvmProvider;
228
+ bitcoinProvider: VultisigUtxoProvider;
229
+ solanaProvider: VultisigSolanaProvider;
230
+ thorchainProvider: VultisigOfflineProvider;
231
+ cosmosProvider: VultisigOfflineProvider;
232
+ }) {
233
+ super();
234
+
235
+ this.evmProvider = providers.evmProvider;
236
+ this.bitcoinProvider = providers.bitcoinProvider;
237
+ this.solanaProvider = providers.solanaProvider;
238
+ this.thorchainProvider = providers.thorchainProvider;
239
+ this.cosmosProvider = providers.cosmosProvider;
240
+ }
241
+
242
+ transport?: core.Transport | undefined;
243
+
244
+ public async getDeviceID(): Promise<string> {
245
+ return "vultisig:" + (await this.getVault());
246
+ }
247
+
248
+ async getFeatures(): Promise<Record<string, any>> {
249
+ return {};
250
+ }
251
+
252
+ public async getFirmwareVersion(): Promise<string> {
253
+ return "vultisig";
254
+ }
255
+
256
+ public async getModel(): Promise<string> {
257
+ return "Vultisig";
258
+ }
259
+
260
+ public async getLabel(): Promise<string> {
261
+ return "Vultisig";
262
+ }
263
+
264
+ public async isInitialized(): Promise<boolean> {
265
+ return true;
266
+ }
267
+
268
+ public async isLocked(): Promise<boolean> {
269
+ return false;
270
+ }
271
+
272
+ public async clearSession(): Promise<void> {}
273
+
274
+ public async initialize(): Promise<void> {}
275
+
276
+ public async ping(msg: core.Ping): Promise<core.Pong> {
277
+ return { msg: msg.msg };
278
+ }
279
+
280
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
281
+ public async sendPin(pin: string): Promise<void> {}
282
+
283
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
284
+ public async sendPassphrase(passphrase: string): Promise<void> {}
285
+
286
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
287
+ public async sendCharacter(charater: string): Promise<void> {}
288
+
289
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
290
+ public async sendWord(word: string): Promise<void> {}
291
+
292
+ public async cancel(): Promise<void> {}
293
+
294
+ public async wipe(): Promise<void> {}
295
+
296
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
297
+ public async reset(msg: core.ResetDevice): Promise<void> {}
298
+
299
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
300
+ public async recover(msg: core.RecoverDevice): Promise<void> {}
301
+
302
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
303
+ public async loadDevice(msg: core.LoadDevice): Promise<void> {}
304
+
305
+ public async disconnect(): Promise<void> {}
306
+
307
+ public async getPublicKeys(msg: Array<core.GetPublicKey>): Promise<Array<core.PublicKey | null>> {
308
+ return await Promise.all(
309
+ msg.map(async (getPublicKey) => {
310
+ const { coin, scriptType } = getPublicKey;
311
+
312
+ if (scriptType !== undefined) {
313
+ const isSupported = await this.btcSupportsScriptType(coin, scriptType);
314
+ if (!isSupported) {
315
+ return null;
316
+ }
317
+ }
318
+
319
+ switch (coin) {
320
+ case "Bitcoin": {
321
+ // Note this is a pubKey, not an xpub, however vultisig does not support utxo derivation,
322
+ // so this functions as an account (xpub) for all intents and purposes
323
+ const pubKey = await this.btcGetAddress({ coin, scriptType } as core.BTCGetAddress);
324
+ return { xpub: pubKey } as core.PublicKey;
325
+ }
326
+ default:
327
+ break;
328
+ }
329
+ throw new Error("Vultisig does not support");
330
+ })
331
+ );
332
+ }
333
+
334
+ /** Ethereum */
335
+
336
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
337
+ public async ethGetAddress(_msg: core.ETHGetAddress): Promise<core.Address | null> {
338
+ const address = await eth.ethGetAddress(this.evmProvider);
339
+
340
+ if (address) {
341
+ this.ethAddress = address;
342
+ return address;
343
+ } else {
344
+ this.ethAddress = null;
345
+ return null;
346
+ }
347
+ }
348
+
349
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
350
+ public async ethSignTx(msg: core.ETHSignTx): Promise<core.ETHSignedTx | null> {
351
+ console.error("Method not implemented");
352
+ return null;
353
+ }
354
+
355
+ public async ethSendTx(msg: core.ETHSignTx): Promise<core.ETHTxHash | null> {
356
+ const address = await this.ethGetAddress({ addressNList: [] });
357
+ return address ? eth.ethSendTx(msg, this.evmProvider, address) : null;
358
+ }
359
+
360
+ public async ethSignMessage(msg: core.ETHSignMessage): Promise<core.ETHSignedMessage | null> {
361
+ const address = await this.ethGetAddress({ addressNList: [] });
362
+ return address ? eth.ethSignMessage(msg, this.evmProvider, address) : null;
363
+ }
364
+
365
+ async ethSignTypedData(msg: core.ETHSignTypedData): Promise<core.ETHSignedTypedData | null> {
366
+ const address = await this.ethGetAddress({ addressNList: [] });
367
+ return address ? eth.ethSignTypedData(msg, this.evmProvider, address) : null;
368
+ }
369
+
370
+ public async ethVerifyMessage(msg: core.ETHVerifyMessage): Promise<boolean | null> {
371
+ if (!msg.signature.startsWith("0x")) msg.signature = `0x${msg.signature}`;
372
+ const digest = keccak256(core.buildMessage(msg.message));
373
+ return recoverAddress(digest, msg.signature) === msg.address;
374
+ }
375
+
376
+ public async ethGetChainId(): Promise<number | null> {
377
+ return eth.ethGetChainId(this.evmProvider);
378
+ }
379
+
380
+ public async ethAddChain(params: core.AddEthereumChainParameter): Promise<void> {
381
+ return eth.ethAddChain(this.evmProvider, params);
382
+ }
383
+
384
+ public async ethSwitchChain(params: core.AddEthereumChainParameter): Promise<void> {
385
+ return eth.ethSwitchChain(this.evmProvider, params);
386
+ }
387
+
388
+ /** Bitcoin */
389
+
390
+ public async btcGetAddress(msg: core.BTCGetAddress): Promise<string | null> {
391
+ return btc.btcGetAddress(this.bitcoinProvider, msg);
392
+ }
393
+
394
+ public async btcSignTx(msg: core.BTCSignTx): Promise<core.BTCSignedTx | null> {
395
+ const { coin } = msg;
396
+ switch (coin) {
397
+ case "Bitcoin":
398
+ return btc.bitcoinSignTx(msg, this.bitcoinProvider);
399
+ default:
400
+ throw new Error("Vultisig does not support");
401
+ }
402
+ }
403
+
404
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
405
+ public async btcSignMessage(msg: core.BTCSignMessage): Promise<core.BTCSignedMessage | null> {
406
+ throw new Error("not supported");
407
+ }
408
+
409
+ public async btcVerifyMessage(msg: core.BTCVerifyMessage): Promise<boolean | null> {
410
+ const signature = Base64.fromByteArray(core.fromHexString(msg.signature));
411
+ return bitcoinMsg.verify(msg.message, msg.address, signature);
412
+ }
413
+
414
+ /** get vault */
415
+
416
+ public async getVault(): Promise<string | null> {
417
+ const vault = await window.vultisig?.getVault();
418
+ return vault?.uid || null;
419
+ }
420
+
421
+ /** Solana */
422
+
423
+ public async solanaGetAddress(): Promise<string | null> {
424
+ const { publicKey } = await this.solanaProvider.connect();
425
+ return publicKey.toString();
426
+ }
427
+
428
+ public async solanaSignTx(msg: core.SolanaSignTx): Promise<core.SolanaSignedTx | null> {
429
+ const address = await this.solanaGetAddress();
430
+ return address ? solanaSignTx(msg, this.solanaProvider, address) : null;
431
+ }
432
+
433
+ public async solanaSendTx(msg: core.SolanaSignTx): Promise<core.SolanaTxSignature | null> {
434
+ const address = await this.solanaGetAddress();
435
+ return address ? solanaSendTx(msg, this.solanaProvider, address) : null;
436
+ }
437
+
438
+ /** THORChain */
439
+
440
+ public async thorchainGetAddress(): Promise<string | null> {
441
+ const address = await thorchain.thorchainGetAddress(this.thorchainProvider);
442
+ return address || null;
443
+ }
444
+
445
+ public async thorchainSignTx(msg: core.ThorchainSignTx): Promise<core.ThorchainSignedTx | null> {
446
+ return thorchain.thorchainSignTx(this.thorchainProvider, msg);
447
+ }
448
+
449
+ /** Cosmos */
450
+
451
+ public async cosmosGetAddress(): Promise<string | null> {
452
+ const address = await cosmos.cosmosGetAddress(this.cosmosProvider);
453
+ return address || null;
454
+ }
455
+
456
+ public async cosmosSignTx(msg: core.CosmosSignTx): Promise<core.CosmosSignedTx | null> {
457
+ return cosmos.cosmosSignTx(this.cosmosProvider, msg);
458
+ }
459
+ }