@drift-labs/vaults-sdk 0.1.487 → 0.1.489

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.
@@ -0,0 +1,49 @@
1
+ import assert from 'assert';
2
+
3
+ import { parseKeypairUrl } from './ledgerWallet';
4
+
5
+ assert.deepStrictEqual(parseKeypairUrl(''), {
6
+ walletId: undefined,
7
+ account: undefined,
8
+ change: undefined,
9
+ });
10
+
11
+ assert.deepStrictEqual(parseKeypairUrl('usb://ledger'), {
12
+ walletId: undefined,
13
+ account: undefined,
14
+ change: undefined,
15
+ });
16
+
17
+ assert.deepStrictEqual(parseKeypairUrl('usb://ledger?key=1'), {
18
+ walletId: undefined,
19
+ account: 1,
20
+ change: undefined,
21
+ });
22
+
23
+ assert.deepStrictEqual(parseKeypairUrl('usb://ledger?key=1/2'), {
24
+ walletId: undefined,
25
+ account: 1,
26
+ change: 2,
27
+ });
28
+
29
+ assert.deepStrictEqual(
30
+ parseKeypairUrl(
31
+ 'usb://ledger/BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK?key=0'
32
+ ),
33
+ {
34
+ walletId: 'BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK',
35
+ account: 0,
36
+ change: undefined,
37
+ }
38
+ );
39
+
40
+ assert.deepStrictEqual(
41
+ parseKeypairUrl(
42
+ 'usb://ledger/BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK?key=0/0'
43
+ ),
44
+ {
45
+ walletId: 'BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK',
46
+ account: 0,
47
+ change: 0,
48
+ }
49
+ );
@@ -0,0 +1,111 @@
1
+ import Solana from '@ledgerhq/hw-app-solana';
2
+ import type { default as Transport } from '@ledgerhq/hw-transport';
3
+ import TransportNodeHid from '@ledgerhq/hw-transport-node-hid';
4
+ import { getDevices } from '@ledgerhq/hw-transport-node-hid-noevents';
5
+ import {
6
+ LedgerWalletAdapter,
7
+ getDerivationPath,
8
+ } from '@solana/wallet-adapter-ledger';
9
+ import { PublicKey, Keypair } from '@solana/web3.js';
10
+ import type { Wallet } from '@drift-labs/sdk';
11
+
12
+ // Follows solana cli url format
13
+ // usb://<MANUFACTURER>[/<WALLET_ID>][?key=<ACCOUNT>[/<CHANGE>]]
14
+ // See: https://docs.solanalabs.com/cli/intro#hardware-wallet
15
+ export const parseKeypairUrl = (
16
+ url = ''
17
+ ): {
18
+ walletId?: string;
19
+ account?: number;
20
+ change?: number;
21
+ } => {
22
+ const walletId = url.match(/(?<=usb:\/\/ledger\/)(\w+)?/)?.[0];
23
+ const [account, change] = (url.split('?key=')[1]?.split('/') ?? []).map(
24
+ Number
25
+ );
26
+ return {
27
+ walletId,
28
+ account,
29
+ change,
30
+ };
31
+ };
32
+
33
+ async function getPublicKey(
34
+ transport: Transport,
35
+ account?: number,
36
+ change?: number
37
+ ): Promise<PublicKey> {
38
+ const path =
39
+ "44'/501'" + // Following BIP44 standard
40
+ (account !== undefined ? `/${account}` : '') +
41
+ (change !== undefined ? `/${change}` : '');
42
+
43
+ const { address } = await new Solana(transport).getAddress(path);
44
+ return new PublicKey(new Uint8Array(address));
45
+ }
46
+
47
+ /*
48
+ * Returns a Drift compatible wallet backed by ledger hardware device
49
+ * This only works in an nodejs environment, based on the transport used
50
+ *
51
+ * Key derivation path is set based on:
52
+ * See: https://docs.solanalabs.com/cli/intro#hardware-wallet
53
+ */
54
+ export async function getLedgerWallet(url = ''): Promise<Wallet> {
55
+ const { account, change, walletId } = parseKeypairUrl(url);
56
+
57
+ const derivationPath = getDerivationPath(account, change);
58
+
59
+ // Load the first device
60
+ let transport = await TransportNodeHid.open('');
61
+
62
+ // If walletId is specified, we need to loop and correct device.
63
+ if (walletId) {
64
+ const devices = getDevices();
65
+ let correctDeviceFound = false;
66
+
67
+ for (const device of devices) {
68
+ // Wallet id is the public key of the device (with no account or change)
69
+ const connectedWalletId = await getPublicKey(
70
+ transport,
71
+ undefined,
72
+ undefined
73
+ );
74
+
75
+ if (connectedWalletId.toString() === walletId) {
76
+ correctDeviceFound = true;
77
+ break;
78
+ }
79
+
80
+ transport.close();
81
+ transport = await TransportNodeHid.open(device.path);
82
+ }
83
+
84
+ if (!correctDeviceFound) {
85
+ throw new Error('Wallet not found');
86
+ }
87
+ }
88
+
89
+ const publicKey = await getPublicKey(transport, account, change);
90
+
91
+ // We can reuse the existing ledger wallet adapter
92
+ // But we need to inject/hack in our own transport (as we not a browser)
93
+ const wallet = new LedgerWalletAdapter({ derivationPath });
94
+
95
+ // Do some hacky things to get the wallet to work
96
+ // These are all done in the `connect` of the ledger wallet adapter
97
+ wallet['_transport'] = transport;
98
+ wallet['_publicKey'] = publicKey;
99
+ transport.on('disconnect', wallet['_disconnected']);
100
+ wallet.emit('connect', publicKey);
101
+
102
+ // Return a Drift compatible wallet
103
+ return {
104
+ payer: undefined as unknown as Keypair, // Doesn't appear to break things
105
+ publicKey: publicKey,
106
+ signTransaction: wallet.signTransaction.bind(wallet),
107
+ signVersionedTransaction: wallet.signTransaction.bind(wallet),
108
+ signAllTransactions: wallet.signAllTransactions.bind(wallet),
109
+ signAllVersionedTransactions: wallet.signAllTransactions.bind(wallet),
110
+ };
111
+ }
package/cli/utils.ts CHANGED
@@ -5,6 +5,9 @@ import { Connection, Keypair } from "@solana/web3.js";
5
5
  import { AnchorProvider, Wallet as AnchorWallet } from "@coral-xyz/anchor";
6
6
  import * as anchor from '@coral-xyz/anchor';
7
7
  import { IDL } from "../src/types/drift_vaults";
8
+ import { getLedgerWallet } from "./ledgerWallet";
9
+ import fs from 'fs';
10
+
8
11
 
9
12
  export async function printVault(slot: number, driftClient: DriftClient, vault: Vault, vaultEquity: BN, spotMarket: SpotMarketAccount, spotOracle: OraclePriceData) {
10
13
 
@@ -117,23 +120,35 @@ export async function getCommandContext(program: Command, needToSign: boolean):
117
120
 
118
121
  const opts = program.opts();
119
122
 
120
- let keypair: Keypair;
121
- if (needToSign) {
123
+ let wallet: Wallet;
124
+ const isLedgerUrl = opts.keypair.startsWith('usb://ledger');
125
+ console.log("isLedgerUrl:", isLedgerUrl);
126
+
127
+ if (isLedgerUrl || fs.existsSync(opts.keypair)) {
128
+ console.log("opts.keypair:", opts.keypair);
129
+ } else {
130
+ console.log("opts.keypair:", opts.keypair.replace(/./g, '*'));
131
+ }
132
+
133
+ wallet = new Wallet(Keypair.generate());
134
+
135
+ if (isLedgerUrl) {
136
+ wallet = await getLedgerWallet(opts.keypair) as unknown as Wallet;
137
+ } else if (opts.keypair) {
122
138
  try {
123
- console.log(opts.keypair);
124
- keypair = loadKeypair(opts.keypair as string);
139
+ const keypair = loadKeypair(opts.keypair as string);
140
+ wallet = new Wallet(keypair);
125
141
  } catch (e) {
126
142
  console.error(`Need to provide a valid keypair: ${e}`);
127
143
  process.exit(1);
128
144
  }
129
145
  } else {
130
- keypair = Keypair.generate();
146
+ if (needToSign) {
147
+ throw new Error("Need to provide a keypair.");
148
+ }
131
149
  }
132
150
 
133
- const wallet = new Wallet(keypair);
134
- if (needToSign) {
135
- console.log(`Signing wallet address: `, wallet.publicKey.toBase58());
136
- }
151
+ console.log(`Loaded wallet address: ${wallet.publicKey.toBase58()}`);
137
152
 
138
153
  const connection = new Connection(opts.url, {
139
154
  commitment: opts.commitment,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/vaults-sdk",
3
- "version": "0.1.487",
3
+ "version": "0.1.489",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "directories": {
@@ -10,6 +10,10 @@
10
10
  "@coral-xyz/anchor": "0.28.0",
11
11
  "@drift-labs/competitions-sdk": "0.2.386",
12
12
  "@drift-labs/sdk": "2.96.0-beta.28",
13
+ "@ledgerhq/hw-app-solana": "^7.1.1",
14
+ "@ledgerhq/hw-transport": "^6.30.1",
15
+ "@ledgerhq/hw-transport-node-hid": "^6.28.1",
16
+ "@solana/wallet-adapter-ledger": "^0.9.25",
13
17
  "@solana/web3.js": "1.92.3",
14
18
  "commander": "^11.0.0",
15
19
  "dotenv": "^16.3.1",