@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.
- package/LICENSE.md +21 -0
- package/dist/adapter.d.ts +22 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +84 -0
- package/dist/adapter.js.map +1 -0
- package/dist/bitcoin.d.ts +6 -0
- package/dist/bitcoin.d.ts.map +1 -0
- package/dist/bitcoin.js +184 -0
- package/dist/bitcoin.js.map +1 -0
- package/dist/cosmos.d.ts +6 -0
- package/dist/cosmos.d.ts.map +1 -0
- package/dist/cosmos.js +47 -0
- package/dist/cosmos.js.map +1 -0
- package/dist/ethereum.d.ts +12 -0
- package/dist/ethereum.d.ts.map +1 -0
- package/dist/ethereum.js +193 -0
- package/dist/ethereum.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/solana.d.ts +9 -0
- package/dist/solana.d.ts.map +1 -0
- package/dist/solana.js +55 -0
- package/dist/solana.js.map +1 -0
- package/dist/thorchain.d.ts +6 -0
- package/dist/thorchain.d.ts.map +1 -0
- package/dist/thorchain.js +46 -0
- package/dist/thorchain.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/vultisig.d.ts +123 -0
- package/dist/vultisig.d.ts.map +1 -0
- package/dist/vultisig.js +482 -0
- package/dist/vultisig.js.map +1 -0
- package/package.json +31 -0
- package/src/adapter.ts +72 -0
- package/src/bitcoin.ts +171 -0
- package/src/cosmos.ts +43 -0
- package/src/ethereum.ts +169 -0
- package/src/index.ts +2 -0
- package/src/solana.ts +31 -0
- package/src/thorchain.ts +43 -0
- package/src/types.ts +62 -0
- package/src/vultisig.test.ts +253 -0
- package/src/vultisig.ts +459 -0
- package/tsconfig.json +10 -0
- 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
|
+
});
|
package/src/vultisig.ts
ADDED
|
@@ -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
|
+
}
|