@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.
- package/cli/ledgerWallet.test.ts +49 -0
- package/cli/ledgerWallet.ts +111 -0
- package/cli/utils.ts +24 -9
- package/package.json +5 -1
|
@@ -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
|
|
121
|
-
|
|
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
|
-
|
|
124
|
-
|
|
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
|
-
|
|
146
|
+
if (needToSign) {
|
|
147
|
+
throw new Error("Need to provide a keypair.");
|
|
148
|
+
}
|
|
131
149
|
}
|
|
132
150
|
|
|
133
|
-
|
|
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.
|
|
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",
|