@velocity-exchange/vaults-sdk 0.0.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/.env.example +3 -0
- package/README.md +152 -0
- package/cli/cli.ts +751 -0
- package/cli/commands/adminDeleteFeeUpdate.ts +73 -0
- package/cli/commands/adminInitFeeUpdate.ts +73 -0
- package/cli/commands/adminUpdateVaultClass.ts +49 -0
- package/cli/commands/applyProfitShare.ts +139 -0
- package/cli/commands/decodeLogs.ts +98 -0
- package/cli/commands/deposit.ts +98 -0
- package/cli/commands/deriveVaultAddress.ts +14 -0
- package/cli/commands/forceWithdraw.ts +56 -0
- package/cli/commands/forceWithdrawAll.ts +142 -0
- package/cli/commands/index.ts +28 -0
- package/cli/commands/initVault.ts +227 -0
- package/cli/commands/initVaultDepositor.ts +42 -0
- package/cli/commands/listDepositorsForVault.ts +32 -0
- package/cli/commands/managerApplyProfitShare.ts +32 -0
- package/cli/commands/managerBorrow.ts +77 -0
- package/cli/commands/managerCancelWithdraw.ts +30 -0
- package/cli/commands/managerDeposit.ts +45 -0
- package/cli/commands/managerRepay.ts +94 -0
- package/cli/commands/managerRequestWithdraw.ts +86 -0
- package/cli/commands/managerUpdateBorrow.ts +56 -0
- package/cli/commands/managerUpdateFees.ts +156 -0
- package/cli/commands/managerUpdateMarginTradingEnabled.ts +32 -0
- package/cli/commands/managerUpdatePoolId.ts +36 -0
- package/cli/commands/managerUpdateVault.ts +210 -0
- package/cli/commands/managerUpdateVaultDelegate.ts +43 -0
- package/cli/commands/managerUpdateVaultManager.ts +77 -0
- package/cli/commands/managerWithdraw.ts +30 -0
- package/cli/commands/requestWithdraw.ts +58 -0
- package/cli/commands/vaultDeposit.ts +42 -0
- package/cli/commands/vaultInvariantChecks.ts +407 -0
- package/cli/commands/vaultWithdraw.ts +42 -0
- package/cli/commands/viewVault.ts +50 -0
- package/cli/commands/viewVaultDepositor.ts +36 -0
- package/cli/commands/withdraw.ts +40 -0
- package/cli/ledgerWallet.test.ts +49 -0
- package/cli/ledgerWallet.ts +111 -0
- package/cli/utils.ts +389 -0
- package/package.json +48 -0
- package/src/accountSubscribers/index.ts +2 -0
- package/src/accountSubscribers/pollingVaultDepositorSubscriber.ts +69 -0
- package/src/accountSubscribers/pollingVaultSubscriber.ts +63 -0
- package/src/accountSubscribers/pollingVaultsProgramAccountSubscriber.ts +114 -0
- package/src/accounts/index.ts +2 -0
- package/src/accounts/vaultAccount.ts +255 -0
- package/src/accounts/vaultDepositorAccount.ts +77 -0
- package/src/accounts/vaultsProgramAccount.ts +38 -0
- package/src/addresses.ts +114 -0
- package/src/constants/index.ts +15 -0
- package/src/idl/drift_vaults.json +5698 -0
- package/src/index.ts +11 -0
- package/src/math/index.ts +2 -0
- package/src/math/vault.ts +71 -0
- package/src/math/vaultDepositor.ts +90 -0
- package/src/name.ts +18 -0
- package/src/parsers/index.ts +1 -0
- package/src/parsers/logParser.ts +28 -0
- package/src/types/drift_vaults.ts +6211 -0
- package/src/types/types.ts +336 -0
- package/src/utils.ts +74 -0
- package/src/vaultClient.ts +3666 -0
- package/tsconfig.json +24 -0
- package/velocity-exchange-vaults-sdk-0.0.1.tgz +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import {
|
|
3
|
+
OptionValues,
|
|
4
|
+
Command
|
|
5
|
+
} from "commander";
|
|
6
|
+
import { getCommandContext, printVault } from "../utils";
|
|
7
|
+
import { FeeUpdate, getFeeUpdateAddressSync } from "../../src";
|
|
8
|
+
|
|
9
|
+
export const viewVault = async (program: Command, cmdOpts: OptionValues) => {
|
|
10
|
+
|
|
11
|
+
let address: PublicKey;
|
|
12
|
+
try {
|
|
13
|
+
address = new PublicKey(cmdOpts.vaultAddress as string);
|
|
14
|
+
} catch (err) {
|
|
15
|
+
console.error("Invalid vault address");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
driftVault,
|
|
21
|
+
driftClient,
|
|
22
|
+
} = await getCommandContext(program, false);
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
const vaultAndSlot = await driftVault.getVaultAndSlot(address);
|
|
26
|
+
const vault = vaultAndSlot.vault;
|
|
27
|
+
const spotMarket = driftClient.getSpotMarketAccount(vault.spotMarketIndex);
|
|
28
|
+
if (!spotMarket) {
|
|
29
|
+
throw new Error(`Spot market ${vault.spotMarketIndex} not found`);
|
|
30
|
+
}
|
|
31
|
+
const spotOracle = driftClient.getOracleDataForSpotMarket(vault.spotMarketIndex);
|
|
32
|
+
if (!spotOracle) {
|
|
33
|
+
throw new Error(`Spot oracle ${vault.spotMarketIndex} not found`);
|
|
34
|
+
}
|
|
35
|
+
const vaultEquity = await driftVault.calculateVaultEquity({
|
|
36
|
+
vault,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
let feeUpdateAccount: FeeUpdate | null = null;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const feeUpdatePubkey = getFeeUpdateAddressSync(driftVault.program.programId, address);
|
|
43
|
+
feeUpdateAccount = await driftVault.getFeeUpdate(feeUpdatePubkey);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
feeUpdateAccount = null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await printVault(vaultAndSlot.slot, driftClient, vault, vaultEquity, spotMarket, spotOracle, feeUpdateAccount);
|
|
49
|
+
};
|
|
50
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import {
|
|
3
|
+
OptionValues,
|
|
4
|
+
Command
|
|
5
|
+
} from "commander";
|
|
6
|
+
import { getCommandContext, printVaultDepositor } from "../utils";
|
|
7
|
+
import { getVaultDepositorAddressSync } from "../../src";
|
|
8
|
+
|
|
9
|
+
export const viewVaultDepositor = async (program: Command, cmdOpts: OptionValues) => {
|
|
10
|
+
|
|
11
|
+
let vaultDepositorAddress: PublicKey;
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
driftVault
|
|
15
|
+
} = await getCommandContext(program, false);
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
if (cmdOpts.vaultDepositorAddress !== undefined) {
|
|
19
|
+
vaultDepositorAddress = new PublicKey(cmdOpts.vaultDepositorAddress as string);
|
|
20
|
+
} else if (cmdOpts.authority !== undefined && cmdOpts.vaultAddress !== undefined) {
|
|
21
|
+
vaultDepositorAddress = getVaultDepositorAddressSync(
|
|
22
|
+
driftVault.program.programId,
|
|
23
|
+
new PublicKey(cmdOpts.vaultAddress as string),
|
|
24
|
+
new PublicKey(cmdOpts.authority as string));
|
|
25
|
+
} else {
|
|
26
|
+
console.error("Must supply --vault-depositor-address or --authority and --vault-address");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.error("Failed to load VaultDepositor address");
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const vaultDepositor = await driftVault.getVaultDepositor(vaultDepositorAddress);
|
|
35
|
+
printVaultDepositor(vaultDepositor);
|
|
36
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import {
|
|
3
|
+
OptionValues,
|
|
4
|
+
Command
|
|
5
|
+
} from "commander";
|
|
6
|
+
import { getCommandContext } from "../utils";
|
|
7
|
+
import { getVaultDepositorAddressSync, VAULT_PROGRAM_ID } from "../../src";
|
|
8
|
+
|
|
9
|
+
export const withdraw = async (program: Command, cmdOpts: OptionValues) => {
|
|
10
|
+
|
|
11
|
+
// verify correct args provided
|
|
12
|
+
if (!cmdOpts.vaultDepositorAddress) {
|
|
13
|
+
if (!cmdOpts.vaultAddress || !cmdOpts.authority) {
|
|
14
|
+
console.error("Must provide --vault-address and --authority if not providing --vault-depositor-address");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
driftVault,
|
|
21
|
+
driftClient
|
|
22
|
+
} = await getCommandContext(program, true);
|
|
23
|
+
|
|
24
|
+
let vaultDepositorAddress: PublicKey;
|
|
25
|
+
if (cmdOpts.vaultDepositorAddress) {
|
|
26
|
+
try {
|
|
27
|
+
vaultDepositorAddress = new PublicKey(cmdOpts.vaultDepositorAddress as string);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.error("Invalid vault depositor address");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
const vaultAddress = new PublicKey(cmdOpts.vaultAddress as string);
|
|
34
|
+
const authority = new PublicKey(cmdOpts.authority as string);
|
|
35
|
+
vaultDepositorAddress = getVaultDepositorAddressSync(VAULT_PROGRAM_ID, vaultAddress, authority);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const tx = await driftVault.withdraw(vaultDepositorAddress);
|
|
39
|
+
console.log(`Withdrew from vault: https://solana.fm/tx/${tx}${driftClient.env === "devnet" ? "?cluster=devnet-solana" : ""}`);
|
|
40
|
+
};
|
|
@@ -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 '@velocity-exchange/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
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BASE_PRECISION,
|
|
3
|
+
BN,
|
|
4
|
+
DriftClient,
|
|
5
|
+
DriftEnv,
|
|
6
|
+
OraclePriceData,
|
|
7
|
+
PRICE_PRECISION,
|
|
8
|
+
QUOTE_PRECISION,
|
|
9
|
+
SpotMarketAccount,
|
|
10
|
+
TEN,
|
|
11
|
+
User,
|
|
12
|
+
Wallet,
|
|
13
|
+
WhileValidTxSender,
|
|
14
|
+
convertToNumber,
|
|
15
|
+
getSignedTokenAmount,
|
|
16
|
+
getTokenAmount,
|
|
17
|
+
loadKeypair,
|
|
18
|
+
} from '@velocity-exchange/sdk';
|
|
19
|
+
import {
|
|
20
|
+
FeeUpdate,
|
|
21
|
+
Vault,
|
|
22
|
+
VaultClass,
|
|
23
|
+
VaultClient,
|
|
24
|
+
VaultDepositor,
|
|
25
|
+
decodeName,
|
|
26
|
+
} from '../src';
|
|
27
|
+
import { Command } from 'commander';
|
|
28
|
+
import {
|
|
29
|
+
Connection,
|
|
30
|
+
Keypair,
|
|
31
|
+
PublicKey,
|
|
32
|
+
Transaction,
|
|
33
|
+
TransactionInstruction,
|
|
34
|
+
} from '@solana/web3.js';
|
|
35
|
+
import { AnchorProvider, Wallet as AnchorWallet } from '@coral-xyz/anchor';
|
|
36
|
+
import * as anchor from '@coral-xyz/anchor';
|
|
37
|
+
import { IDL } from '../src/utils';
|
|
38
|
+
import { DriftVaults } from '../src/types/drift_vaults';
|
|
39
|
+
import { getLedgerWallet } from './ledgerWallet';
|
|
40
|
+
import fs from 'fs';
|
|
41
|
+
import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes';
|
|
42
|
+
|
|
43
|
+
export async function printVault(
|
|
44
|
+
slot: number,
|
|
45
|
+
driftClient: DriftClient,
|
|
46
|
+
vault: Vault,
|
|
47
|
+
vaultEquity: BN,
|
|
48
|
+
spotMarket: SpotMarketAccount,
|
|
49
|
+
spotOracle: OraclePriceData,
|
|
50
|
+
feeUpdateAccount: FeeUpdate | null = null
|
|
51
|
+
) {
|
|
52
|
+
const oraclePriceNum = convertToNumber(spotOracle.price, PRICE_PRECISION);
|
|
53
|
+
const spotPrecision = TEN.pow(new BN(spotMarket.decimals));
|
|
54
|
+
const spotSymbol = decodeName(spotMarket.name);
|
|
55
|
+
|
|
56
|
+
console.log(`slot: ${slot}`);
|
|
57
|
+
console.log(`vault: ${decodeName(vault.name)}`);
|
|
58
|
+
console.log(`vaultClass: ${vault.vaultClass}`);
|
|
59
|
+
if (vault.vaultClass === VaultClass.TRUSTED) {
|
|
60
|
+
console.log(
|
|
61
|
+
` managerBorrowedValue: ${convertToNumber(
|
|
62
|
+
vault.managerBorrowedValue,
|
|
63
|
+
spotPrecision
|
|
64
|
+
)} ${spotSymbol}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
console.log(`pubkey: ${vault.pubkey.toBase58()}`);
|
|
68
|
+
console.log(`manager: ${vault.manager.toBase58()}`);
|
|
69
|
+
console.log(`tokenAccount: ${vault.tokenAccount.toBase58()}`);
|
|
70
|
+
console.log(`driftUserStats: ${vault.userStats.toBase58()}`);
|
|
71
|
+
console.log(`driftUser: ${vault.user.toBase58()}`);
|
|
72
|
+
console.log(`delegate: ${vault.delegate.toBase58()}`);
|
|
73
|
+
console.log(`liqDelegate: ${vault.liquidationDelegate.toBase58()}`);
|
|
74
|
+
console.log(`userShares: ${vault.userShares.toString()}`);
|
|
75
|
+
console.log(`totalShares: ${vault.totalShares.toString()}`);
|
|
76
|
+
const managerShares = vault.totalShares.sub(vault.userShares);
|
|
77
|
+
const managerSharePct =
|
|
78
|
+
managerShares.toNumber() / vault.totalShares.toNumber();
|
|
79
|
+
console.log(
|
|
80
|
+
` [managerShares]: ${managerShares.toString()} (${(
|
|
81
|
+
managerSharePct * 100.0
|
|
82
|
+
).toFixed(4)}%)`
|
|
83
|
+
);
|
|
84
|
+
console.log(`totalShares: ${vault.totalShares.toString()}`);
|
|
85
|
+
console.log(`lastFeeUpdateTs: ${vault.lastFeeUpdateTs.toString()}`);
|
|
86
|
+
console.log(`liquidationStartTs: ${vault.liquidationStartTs.toString()}`);
|
|
87
|
+
console.log(`redeemPeriod: ${vault.redeemPeriod.toString()}`);
|
|
88
|
+
console.log(
|
|
89
|
+
`totalWithdrawRequested: ${vault.totalWithdrawRequested.toString()}`
|
|
90
|
+
);
|
|
91
|
+
console.log(
|
|
92
|
+
`maxTokens: ${convertToNumber(
|
|
93
|
+
vault.maxTokens,
|
|
94
|
+
spotPrecision
|
|
95
|
+
)} ${spotSymbol} (${vault.maxTokens.toString()})`
|
|
96
|
+
);
|
|
97
|
+
console.log(`sharesBase: ${vault.sharesBase}`);
|
|
98
|
+
console.log(`managementFee: ${vault.managementFee.toString()}`);
|
|
99
|
+
console.log(`initTs: ${vault.initTs.toString()}`);
|
|
100
|
+
console.log(
|
|
101
|
+
`netDeposits: ${convertToNumber(
|
|
102
|
+
vault.netDeposits,
|
|
103
|
+
spotPrecision
|
|
104
|
+
)} ${spotSymbol} (${vault.netDeposits.toString()})`
|
|
105
|
+
);
|
|
106
|
+
console.log(
|
|
107
|
+
`totalDeposits: ${convertToNumber(
|
|
108
|
+
vault.totalDeposits,
|
|
109
|
+
spotPrecision
|
|
110
|
+
)} ${spotSymbol} (${vault.totalDeposits.toString()})`
|
|
111
|
+
);
|
|
112
|
+
console.log(
|
|
113
|
+
`totalWithdraws: ${convertToNumber(
|
|
114
|
+
vault.totalWithdraws,
|
|
115
|
+
spotPrecision
|
|
116
|
+
)} ${spotSymbol} (${vault.totalWithdraws.toString()})`
|
|
117
|
+
);
|
|
118
|
+
console.log(
|
|
119
|
+
`managerNetDeposits: ${convertToNumber(
|
|
120
|
+
vault.managerNetDeposits,
|
|
121
|
+
spotPrecision
|
|
122
|
+
)} ${spotSymbol} (${vault.managerNetDeposits.toString()})`
|
|
123
|
+
);
|
|
124
|
+
console.log(
|
|
125
|
+
`managerTotalDeposits: ${convertToNumber(
|
|
126
|
+
vault.managerTotalDeposits,
|
|
127
|
+
spotPrecision
|
|
128
|
+
)} ${spotSymbol} (${vault.managerTotalDeposits.toString()})`
|
|
129
|
+
);
|
|
130
|
+
console.log(
|
|
131
|
+
`managerTotalWithdraws: ${convertToNumber(
|
|
132
|
+
vault.managerTotalWithdraws,
|
|
133
|
+
spotPrecision
|
|
134
|
+
)} ${spotSymbol} (${vault.managerTotalWithdraws.toString()})`
|
|
135
|
+
);
|
|
136
|
+
console.log(
|
|
137
|
+
`managerTotalFee: ${convertToNumber(
|
|
138
|
+
vault.managerTotalFee,
|
|
139
|
+
spotPrecision
|
|
140
|
+
)} ${spotSymbol} (${vault.managerTotalFee.toString()})`
|
|
141
|
+
);
|
|
142
|
+
console.log(
|
|
143
|
+
`managerTotalProfitShare: ${convertToNumber(
|
|
144
|
+
vault.managerTotalProfitShare,
|
|
145
|
+
spotPrecision
|
|
146
|
+
)} ${spotSymbol} (${vault.managerTotalProfitShare.toString()})`
|
|
147
|
+
);
|
|
148
|
+
console.log(`lastManagerWithdrawRequest:`);
|
|
149
|
+
console.log(
|
|
150
|
+
` shares: ${vault.lastManagerWithdrawRequest.shares.toString()}`
|
|
151
|
+
);
|
|
152
|
+
console.log(
|
|
153
|
+
` values: ${convertToNumber(
|
|
154
|
+
vault.lastManagerWithdrawRequest.value,
|
|
155
|
+
spotPrecision
|
|
156
|
+
)} ${spotSymbol} (${vault.lastManagerWithdrawRequest.value.toString()})`
|
|
157
|
+
);
|
|
158
|
+
console.log(` ts: ${vault.lastManagerWithdrawRequest.ts.toString()}`);
|
|
159
|
+
console.log(`FeeUpdate Account:`);
|
|
160
|
+
if (feeUpdateAccount) {
|
|
161
|
+
const timeUntilUpdate = feeUpdateAccount.incomingUpdateTs.sub(
|
|
162
|
+
new BN(Date.now() / 1000)
|
|
163
|
+
);
|
|
164
|
+
console.log(
|
|
165
|
+
` incomingUpdateTs: ${feeUpdateAccount.incomingUpdateTs.toString()} (in ${timeUntilUpdate.toString()} seconds)`
|
|
166
|
+
);
|
|
167
|
+
console.log(
|
|
168
|
+
` incomingManagementFee: ${vault.managementFee.toString()} -> ${feeUpdateAccount.incomingManagementFee.toString()}`
|
|
169
|
+
);
|
|
170
|
+
console.log(
|
|
171
|
+
` incomingProfitShare: ${vault.profitShare} -> ${feeUpdateAccount.incomingProfitShare}`
|
|
172
|
+
);
|
|
173
|
+
console.log(
|
|
174
|
+
` incomingHurdleRate: ${vault.hurdleRate} -> ${feeUpdateAccount.incomingHurdleRate}`
|
|
175
|
+
);
|
|
176
|
+
} else {
|
|
177
|
+
console.log(` None`);
|
|
178
|
+
}
|
|
179
|
+
console.log(`minDepositAmount: ${vault.minDepositAmount.toString()}`);
|
|
180
|
+
console.log(`profitShare: ${vault.profitShare}`);
|
|
181
|
+
console.log(`hurdleRate: ${vault.hurdleRate}`);
|
|
182
|
+
console.log(`spotMarketIndex: ${vault.spotMarketIndex}`);
|
|
183
|
+
console.log(`permissioned: ${vault.permissioned}`);
|
|
184
|
+
|
|
185
|
+
const vaultEquityNum = convertToNumber(vaultEquity, QUOTE_PRECISION);
|
|
186
|
+
const netDepositsNum = convertToNumber(vault.netDeposits, spotPrecision);
|
|
187
|
+
console.log(`vaultEquity (USDC): $${vaultEquityNum}`);
|
|
188
|
+
console.log(`manager share (USDC): $${managerSharePct * vaultEquityNum}`);
|
|
189
|
+
|
|
190
|
+
const vaultEquitySpot = vaultEquityNum / oraclePriceNum;
|
|
191
|
+
|
|
192
|
+
const user = new User({
|
|
193
|
+
// accountSubscription,
|
|
194
|
+
driftClient,
|
|
195
|
+
userAccountPublicKey: vault.user,
|
|
196
|
+
});
|
|
197
|
+
await user.subscribe();
|
|
198
|
+
|
|
199
|
+
for (const spotPos of user.getActiveSpotPositions()) {
|
|
200
|
+
const sm = driftClient.getSpotMarketAccount(spotPos.marketIndex)!;
|
|
201
|
+
const prec = TEN.pow(new BN(sm.decimals));
|
|
202
|
+
const sym = decodeName(sm.name);
|
|
203
|
+
const bal = getSignedTokenAmount(
|
|
204
|
+
getTokenAmount(spotPos.scaledBalance, sm, spotPos.balanceType),
|
|
205
|
+
spotPos.balanceType
|
|
206
|
+
);
|
|
207
|
+
console.log(
|
|
208
|
+
`Spot Position: ${spotPos.marketIndex}, ${convertToNumber(
|
|
209
|
+
bal,
|
|
210
|
+
prec
|
|
211
|
+
)} ${sym}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
for (const perpPos of user.getActivePerpPositions()) {
|
|
216
|
+
console.log(
|
|
217
|
+
`Perp Position: ${perpPos.marketIndex}, base: ${convertToNumber(
|
|
218
|
+
perpPos.baseAssetAmount,
|
|
219
|
+
BASE_PRECISION
|
|
220
|
+
)}, quote: ${convertToNumber(perpPos.quoteAssetAmount, QUOTE_PRECISION)}`
|
|
221
|
+
);
|
|
222
|
+
const upnl = user.getUnrealizedPNL(true, perpPos.marketIndex);
|
|
223
|
+
console.log(` upnl: ${convertToNumber(upnl, QUOTE_PRECISION)}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
console.log(`vaultEquity (${spotSymbol}): ${vaultEquitySpot}`);
|
|
227
|
+
console.log(
|
|
228
|
+
`manager share (${spotSymbol}): ${managerSharePct * vaultEquitySpot}`
|
|
229
|
+
);
|
|
230
|
+
console.log(
|
|
231
|
+
`vault PnL (${spotSymbol}): ${vaultEquitySpot - netDepositsNum}`
|
|
232
|
+
);
|
|
233
|
+
console.log(
|
|
234
|
+
`vault PnL (USD) ${convertToNumber(
|
|
235
|
+
user.getTotalAllTimePnl(),
|
|
236
|
+
QUOTE_PRECISION
|
|
237
|
+
)}`
|
|
238
|
+
);
|
|
239
|
+
console.log(
|
|
240
|
+
`vault PnL (spot) ${
|
|
241
|
+
convertToNumber(user.getTotalAllTimePnl(), QUOTE_PRECISION) /
|
|
242
|
+
oraclePriceNum
|
|
243
|
+
}`
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
managerShares,
|
|
248
|
+
managerSharePct,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function printVaultDepositor(vaultDepositor: VaultDepositor) {
|
|
253
|
+
console.log(`vault: ${vaultDepositor.vault.toBase58()}`);
|
|
254
|
+
console.log(`pubkey: ${vaultDepositor.pubkey.toBase58()}`);
|
|
255
|
+
console.log(`authority: ${vaultDepositor.authority.toBase58()}`);
|
|
256
|
+
console.log(`vaultShares: ${vaultDepositor.vaultShares.toString()}`);
|
|
257
|
+
console.log(
|
|
258
|
+
`lastWithdrawRequest.Shares: ${vaultDepositor.lastWithdrawRequest.shares.toString()}`
|
|
259
|
+
);
|
|
260
|
+
console.log(
|
|
261
|
+
`lastWithdrawRequest.Value: ${vaultDepositor.lastWithdrawRequest.value.toString()}`
|
|
262
|
+
);
|
|
263
|
+
console.log(
|
|
264
|
+
`lastWithdrawRequest.Ts: ${vaultDepositor.lastWithdrawRequest.ts.toString()}`
|
|
265
|
+
);
|
|
266
|
+
console.log(
|
|
267
|
+
`lastValidTs: ${vaultDepositor.lastValidTs.toString()}`
|
|
268
|
+
);
|
|
269
|
+
console.log(
|
|
270
|
+
`netDeposits: ${vaultDepositor.netDeposits.toString()}`
|
|
271
|
+
);
|
|
272
|
+
console.log(
|
|
273
|
+
`totalDeposits: ${vaultDepositor.totalDeposits.toString()}`
|
|
274
|
+
);
|
|
275
|
+
console.log(
|
|
276
|
+
`totalWithdraws: ${vaultDepositor.totalWithdraws.toString()}`
|
|
277
|
+
);
|
|
278
|
+
console.log(
|
|
279
|
+
`cumulativeProfitShareAmount: ${vaultDepositor.cumulativeProfitShareAmount.toString()}`
|
|
280
|
+
);
|
|
281
|
+
console.log(
|
|
282
|
+
`vaultSharesBase: ${vaultDepositor.vaultSharesBase.toString()}`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export async function getCommandContext(
|
|
287
|
+
program: Command,
|
|
288
|
+
needToSign: boolean
|
|
289
|
+
): Promise<{
|
|
290
|
+
driftClient: DriftClient;
|
|
291
|
+
driftVault: VaultClient;
|
|
292
|
+
wallet: Wallet;
|
|
293
|
+
}> {
|
|
294
|
+
const opts = program.opts();
|
|
295
|
+
|
|
296
|
+
let wallet: Wallet;
|
|
297
|
+
const isLedgerUrl = opts.keypair.startsWith('usb://ledger');
|
|
298
|
+
console.log('isLedgerUrl:', isLedgerUrl);
|
|
299
|
+
|
|
300
|
+
if (isLedgerUrl || fs.existsSync(opts.keypair)) {
|
|
301
|
+
console.log('opts.keypair:', opts.keypair);
|
|
302
|
+
} else {
|
|
303
|
+
console.log('opts.keypair:', opts.keypair.replace(/./g, '*'));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
let loadedKeypair = false;
|
|
307
|
+
wallet = new Wallet(Keypair.generate());
|
|
308
|
+
|
|
309
|
+
if (isLedgerUrl) {
|
|
310
|
+
wallet = (await getLedgerWallet(opts.keypair)) as unknown as Wallet;
|
|
311
|
+
loadedKeypair = true;
|
|
312
|
+
} else if (opts.keypair) {
|
|
313
|
+
try {
|
|
314
|
+
const keypair = loadKeypair(opts.keypair as string);
|
|
315
|
+
wallet = new Wallet(keypair);
|
|
316
|
+
loadedKeypair = true;
|
|
317
|
+
} catch (e) {
|
|
318
|
+
console.error(`Need to provide a valid keypair: ${e}`);
|
|
319
|
+
process.exit(1);
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
if (needToSign) {
|
|
323
|
+
throw new Error('Need to provide a keypair.');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const driftEnv = opts.env as DriftEnv;
|
|
328
|
+
console.log('driftEnv:', driftEnv);
|
|
329
|
+
|
|
330
|
+
if (loadedKeypair) {
|
|
331
|
+
console.log(`Loaded wallet address: ${wallet.publicKey.toBase58()}`);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const connection = new Connection(opts.url, {
|
|
335
|
+
commitment: opts.commitment,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const driftClient = new DriftClient({
|
|
339
|
+
connection,
|
|
340
|
+
wallet,
|
|
341
|
+
env: driftEnv as DriftEnv,
|
|
342
|
+
opts: {
|
|
343
|
+
commitment: opts.commitment,
|
|
344
|
+
skipPreflight: false,
|
|
345
|
+
preflightCommitment: opts.commitment,
|
|
346
|
+
},
|
|
347
|
+
txSender: new WhileValidTxSender({
|
|
348
|
+
connection,
|
|
349
|
+
wallet,
|
|
350
|
+
opts: {
|
|
351
|
+
maxRetries: 0,
|
|
352
|
+
},
|
|
353
|
+
retrySleep: 1000,
|
|
354
|
+
}),
|
|
355
|
+
});
|
|
356
|
+
await driftClient.subscribe();
|
|
357
|
+
|
|
358
|
+
const provider = new AnchorProvider(connection, wallet as AnchorWallet, {});
|
|
359
|
+
anchor.setProvider(provider);
|
|
360
|
+
const vaultProgram = new anchor.Program<DriftVaults>(IDL, provider);
|
|
361
|
+
|
|
362
|
+
const driftVault = new VaultClient({
|
|
363
|
+
driftClient,
|
|
364
|
+
program: vaultProgram,
|
|
365
|
+
cliMode: true,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
return {
|
|
369
|
+
driftClient,
|
|
370
|
+
driftVault,
|
|
371
|
+
wallet,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/// a valid blockhash to allow tx simulation
|
|
376
|
+
export const BLOCKHASH_PLACEHOLDER =
|
|
377
|
+
'CHNfFRxxeCTyy1RRnKGsV2dkrJfKwVEa8zE7cZgK4TSe';
|
|
378
|
+
|
|
379
|
+
export function dumpTransactionMessage(
|
|
380
|
+
payer: PublicKey,
|
|
381
|
+
ixs: Array<TransactionInstruction>
|
|
382
|
+
): string {
|
|
383
|
+
const tx = new Transaction();
|
|
384
|
+
tx.add(...ixs);
|
|
385
|
+
tx.feePayer = payer;
|
|
386
|
+
tx.recentBlockhash = BLOCKHASH_PLACEHOLDER;
|
|
387
|
+
|
|
388
|
+
return bs58.encode(tx.serialize({ requireAllSignatures: false }));
|
|
389
|
+
}
|