@quartz-labs/sdk 0.0.2 → 0.0.3
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/{build → dist}/client.d.ts +1 -1
- package/{build → dist}/client.js +5 -5
- package/{build → dist}/config/constants.d.ts +2 -0
- package/{src/config/constants.ts → dist/config/constants.js} +1 -5
- package/{src/index.ts → dist/index.d.ts} +1 -2
- package/{build → dist}/index.js +1 -1
- package/{build → dist}/model/driftUser.d.ts +3 -0
- package/{build → dist}/model/driftUser.js +9 -2
- package/dist/user.d.ts +25 -0
- package/dist/user.js +158 -0
- package/dist/utils/helpers.d.ts +12 -0
- package/{build → dist/utils}/helpers.js +8 -8
- package/dist/utils/jupiter.d.ts +7 -0
- package/dist/utils/jupiter.js +48 -0
- package/package.json +6 -3
- package/build/config/constants.js +0 -7
- package/build/helpers.d.ts +0 -12
- package/build/index.d.ts +0 -4
- package/build/user.d.ts +0 -16
- package/build/user.js +0 -47
- package/jest.config.js +0 -4
- package/src/client.ts +0 -119
- package/src/idl/quartz.json +0 -646
- package/src/model/driftUser.ts +0 -1066
- package/src/services/driftClientService.ts +0 -37
- package/src/tests/helpers.test.ts +0 -48
- package/src/types/quartz.ts +0 -1293
- package/src/user.ts +0 -227
- package/src/utils/helpers.ts +0 -68
- package/src/utils/jupiter.ts +0 -73
- package/tsconfig.json +0 -17
- /package/{build → dist}/idl/quartz.json +0 -0
- /package/{build → dist}/services/driftClientService.d.ts +0 -0
- /package/{build → dist}/services/driftClientService.js +0 -0
- /package/{build → dist}/types/quartz.d.ts +0 -0
- /package/{build → dist}/types/quartz.js +0 -0
|
@@ -8,7 +8,7 @@ export declare class QuartzClient {
|
|
|
8
8
|
private connection;
|
|
9
9
|
private wallet;
|
|
10
10
|
private program;
|
|
11
|
-
private
|
|
11
|
+
private quartzLookupTable;
|
|
12
12
|
private driftClient;
|
|
13
13
|
private oracles;
|
|
14
14
|
constructor(connection: Connection, wallet: Wallet, program: Program<Quartz>, quartzAddressTable: AddressLookupTableAccount, driftClient: DriftClient, oracles: Map<string, PublicKey>);
|
package/{build → dist}/client.js
RENAMED
|
@@ -4,14 +4,14 @@ import quartzIdl from "./idl/quartz.json";
|
|
|
4
4
|
import { AnchorProvider, Program, setProvider } from "@coral-xyz/anchor";
|
|
5
5
|
import { PythSolanaReceiver } from "@pythnetwork/pyth-solana-receiver";
|
|
6
6
|
import { QuartzUser } from "./user";
|
|
7
|
-
import {
|
|
7
|
+
import { getDriftUserPublicKey } from "./utils/helpers";
|
|
8
8
|
import { DriftClientService } from "./services/driftClientService";
|
|
9
9
|
export class QuartzClient {
|
|
10
10
|
constructor(connection, wallet, program, quartzAddressTable, driftClient, oracles) {
|
|
11
11
|
this.connection = connection;
|
|
12
12
|
this.wallet = wallet;
|
|
13
13
|
this.program = program;
|
|
14
|
-
this.
|
|
14
|
+
this.quartzLookupTable = quartzAddressTable;
|
|
15
15
|
this.driftClient = driftClient;
|
|
16
16
|
this.oracles = oracles;
|
|
17
17
|
}
|
|
@@ -35,7 +35,7 @@ export class QuartzClient {
|
|
|
35
35
|
}
|
|
36
36
|
async getQuartzAccount(vault) {
|
|
37
37
|
await this.program.account.vault.fetch(vault); // Check account exists
|
|
38
|
-
return new QuartzUser(vault, this.connection, this.driftClient);
|
|
38
|
+
return new QuartzUser(vault, this.connection, this.program, this.quartzLookupTable, this.oracles, this.driftClient);
|
|
39
39
|
}
|
|
40
40
|
async getMultipleQuartzAccounts(vaults) {
|
|
41
41
|
if (vaults.length === 0)
|
|
@@ -45,7 +45,7 @@ export class QuartzClient {
|
|
|
45
45
|
if (account === null)
|
|
46
46
|
throw Error(`Account not found for pubkey: ${vaults[index].toBase58()}`);
|
|
47
47
|
});
|
|
48
|
-
const driftUsers = await fetchDriftAccountsUsingKeys(this.connection, this.driftClient.program, vaults.map((vault) =>
|
|
48
|
+
const driftUsers = await fetchDriftAccountsUsingKeys(this.connection, this.driftClient.program, vaults.map((vault) => getDriftUserPublicKey(vault)));
|
|
49
49
|
// TODO: Uncomment once Drift accounts are guaranteed
|
|
50
50
|
// const undefinedIndex = driftUsers.findIndex(user => !user);
|
|
51
51
|
// if (undefinedIndex !== -1) {
|
|
@@ -54,7 +54,7 @@ export class QuartzClient {
|
|
|
54
54
|
return driftUsers.map((driftUser, index) => {
|
|
55
55
|
if (driftUser === undefined)
|
|
56
56
|
return null;
|
|
57
|
-
return new QuartzUser(vaults[index], this.connection, this.
|
|
57
|
+
return new QuartzUser(vaults[index], this.connection, this.program, this.quartzLookupTable, this.oracles, this.driftClient);
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
}
|
|
@@ -2,6 +2,8 @@ import { PublicKey } from "@solana/web3.js";
|
|
|
2
2
|
export declare const QUARTZ_PROGRAM_ID: PublicKey;
|
|
3
3
|
export declare const QUARTZ_ADDRESS_TABLE: PublicKey;
|
|
4
4
|
export declare const QUARTZ_HEALTH_BUFFER = 0.1;
|
|
5
|
+
export declare const USDC_MINT: PublicKey;
|
|
6
|
+
export declare const WSOL_MINT: PublicKey;
|
|
5
7
|
export declare const DRIFT_MARKET_INDEX_USDC = 0;
|
|
6
8
|
export declare const DRIFT_MARKET_INDEX_SOL = 1;
|
|
7
9
|
export declare const SUPPORTED_DRIFT_MARKETS: number[];
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { PublicKey } from "@solana/web3.js";
|
|
2
|
-
|
|
3
2
|
export const QUARTZ_PROGRAM_ID = new PublicKey("6JjHXLheGSNvvexgzMthEcgjkcirDrGduc3HAKB2P1v2");
|
|
4
3
|
export const QUARTZ_ADDRESS_TABLE = new PublicKey("96BmeKKVGX3LKYSKo3FCEom1YpNY11kCnGscKq6ouxLx");
|
|
5
|
-
|
|
6
4
|
export const QUARTZ_HEALTH_BUFFER = 0.1;
|
|
7
|
-
|
|
8
5
|
export const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
9
6
|
export const WSOL_MINT = new PublicKey("So11111111111111111111111111111111111111112");
|
|
10
|
-
|
|
11
7
|
export const DRIFT_MARKET_INDEX_USDC = 0;
|
|
12
8
|
export const DRIFT_MARKET_INDEX_SOL = 1;
|
|
13
|
-
export const SUPPORTED_DRIFT_MARKETS = [DRIFT_MARKET_INDEX_USDC, DRIFT_MARKET_INDEX_SOL];
|
|
9
|
+
export const SUPPORTED_DRIFT_MARKETS = [DRIFT_MARKET_INDEX_USDC, DRIFT_MARKET_INDEX_SOL];
|
package/{build → dist}/index.js
RENAMED
|
@@ -6,8 +6,11 @@ export declare class DriftUser {
|
|
|
6
6
|
private connection;
|
|
7
7
|
private driftClient;
|
|
8
8
|
private userAccount;
|
|
9
|
+
pubkey: PublicKey;
|
|
10
|
+
statsPubkey: PublicKey;
|
|
9
11
|
constructor(authority: PublicKey, connection: Connection, driftClient: DriftClient, userAccount?: UserAccount);
|
|
10
12
|
initialize(): Promise<void>;
|
|
13
|
+
getDriftUserAccount(): UserAccount;
|
|
11
14
|
getHealth(): number;
|
|
12
15
|
getTokenAmount(marketIndex: number): BN;
|
|
13
16
|
getWithdrawalLimit(marketIndex: number, reduceOnly?: boolean): BN;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { AMM_RESERVE_PRECISION, AMM_RESERVE_PRECISION_EXP, BN, calculateAssetWeight, calculateLiabilityWeight, calculateLiveOracleTwap, calculateMarketMarginRatio, calculateMarketOpenBidAsk, calculatePerpLiabilityValue, calculatePositionPNL, calculateUnrealizedAssetWeight, calculateUnsettledFundingPnl, calculateWithdrawLimit, calculateWorstCasePerpLiabilityValue, fetchUserAccountsUsingKeys, FIVE_MINUTE, getSignedTokenAmount, getStrictTokenValue, getTokenAmount, getWorstCaseTokenAmounts, isSpotPositionAvailable, isVariant, MARGIN_PRECISION, ONE, OPEN_ORDER_MARGIN_REQUIREMENT, PRICE_PRECISION, QUOTE_PRECISION, QUOTE_SPOT_MARKET_INDEX, SPOT_MARKET_WEIGHT_PRECISION, SpotBalanceType, StrictOraclePrice, UserStatus, ZERO, TEN, divCeil } from "@drift-labs/sdk";
|
|
2
|
-
import {
|
|
2
|
+
import { getDriftUserPublicKey, getDriftUserStatsPublicKey } from "../utils/helpers.js";
|
|
3
3
|
export class DriftUser {
|
|
4
4
|
constructor(authority, connection, driftClient, userAccount) {
|
|
5
5
|
this.isInitialized = false;
|
|
6
6
|
this.authority = authority;
|
|
7
7
|
this.connection = connection;
|
|
8
8
|
this.driftClient = driftClient;
|
|
9
|
+
this.pubkey = getDriftUserPublicKey(this.authority);
|
|
10
|
+
this.statsPubkey = getDriftUserStatsPublicKey(this.authority);
|
|
9
11
|
if (userAccount) {
|
|
10
12
|
this.userAccount = userAccount;
|
|
11
13
|
this.isInitialized = true;
|
|
@@ -14,12 +16,17 @@ export class DriftUser {
|
|
|
14
16
|
async initialize() {
|
|
15
17
|
if (this.isInitialized)
|
|
16
18
|
return;
|
|
17
|
-
const [userAccount] = await fetchUserAccountsUsingKeys(this.connection, this.driftClient.program, [
|
|
19
|
+
const [userAccount] = await fetchUserAccountsUsingKeys(this.connection, this.driftClient.program, [this.pubkey]);
|
|
18
20
|
if (!userAccount)
|
|
19
21
|
throw new Error("Drift user not found");
|
|
20
22
|
this.userAccount = userAccount;
|
|
21
23
|
this.isInitialized = true;
|
|
22
24
|
}
|
|
25
|
+
getDriftUserAccount() {
|
|
26
|
+
if (!this.isInitialized)
|
|
27
|
+
throw new Error("DriftUser not initialized");
|
|
28
|
+
return this.userAccount;
|
|
29
|
+
}
|
|
23
30
|
getHealth() {
|
|
24
31
|
if (!this.isInitialized)
|
|
25
32
|
throw new Error("DriftUser not initialized");
|
package/dist/user.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AddressLookupTableAccount, PublicKey, TransactionInstruction } from "@solana/web3.js";
|
|
2
|
+
import { DriftClient, QuoteResponse, UserAccount } from "@drift-labs/sdk";
|
|
3
|
+
import { Connection } from "@solana/web3.js";
|
|
4
|
+
import { Quartz } from "./types/quartz";
|
|
5
|
+
import { Program } from "@coral-xyz/anchor";
|
|
6
|
+
export declare class QuartzUser {
|
|
7
|
+
private pubkey;
|
|
8
|
+
private vaultPubkey;
|
|
9
|
+
private connection;
|
|
10
|
+
private program;
|
|
11
|
+
private quartzLookupTable;
|
|
12
|
+
private oracles;
|
|
13
|
+
private driftClient;
|
|
14
|
+
private driftUser;
|
|
15
|
+
constructor(pubkey: PublicKey, connection: Connection, program: Program<Quartz>, quartzLookupTable: AddressLookupTableAccount, oracles: Map<string, PublicKey>, driftClient: DriftClient, driftUserAccount?: UserAccount);
|
|
16
|
+
private convertToQuartzHealth;
|
|
17
|
+
getHealth(): number;
|
|
18
|
+
getRepayAmountForTargetHealth(targetHealth: number, repayCollateralWeight: number): number;
|
|
19
|
+
getTotalWeightedCollateral(): number;
|
|
20
|
+
getMaintenanceMarginRequirement(): number;
|
|
21
|
+
makeCollateralRepayIxs(caller: PublicKey, callerDepositSpl: PublicKey, callerWithdrawSpl: PublicKey, callerStartingDepositBalance: number, jupiterExactOutRouteQuote: QuoteResponse): Promise<{
|
|
22
|
+
ixs: TransactionInstruction[];
|
|
23
|
+
lookupTables: AddressLookupTableAccount[];
|
|
24
|
+
}>;
|
|
25
|
+
}
|
package/dist/user.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { PublicKey, SystemProgram, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js";
|
|
2
|
+
import { DriftUser } from "./model/driftUser";
|
|
3
|
+
import { BN, DRIFT_PROGRAM_ID } from "@drift-labs/sdk";
|
|
4
|
+
import { getDriftSpotMarketPublicKey, getDriftStatePublicKey, getVaultPublicKey, getVaultSplPublicKey } from "./utils/helpers";
|
|
5
|
+
import { DRIFT_MARKET_INDEX_SOL, DRIFT_MARKET_INDEX_USDC, QUARTZ_HEALTH_BUFFER, USDC_MINT, WSOL_MINT } from "./config/constants";
|
|
6
|
+
import { TOKEN_PROGRAM_ID } from "@coral-xyz/anchor/dist/cjs/utils/token";
|
|
7
|
+
import { ASSOCIATED_TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
|
8
|
+
import { SwapMode } from "@jup-ag/api";
|
|
9
|
+
import { getJupiterSwapIx } from "./utils/jupiter";
|
|
10
|
+
export class QuartzUser {
|
|
11
|
+
constructor(pubkey, connection, program, quartzLookupTable, oracles, driftClient, driftUserAccount) {
|
|
12
|
+
this.pubkey = pubkey;
|
|
13
|
+
this.vaultPubkey = getVaultPublicKey(pubkey);
|
|
14
|
+
this.connection = connection;
|
|
15
|
+
this.program = program;
|
|
16
|
+
this.quartzLookupTable = quartzLookupTable;
|
|
17
|
+
this.oracles = oracles;
|
|
18
|
+
this.driftClient = driftClient;
|
|
19
|
+
this.driftUser = new DriftUser(this.vaultPubkey, connection, driftClient, driftUserAccount);
|
|
20
|
+
}
|
|
21
|
+
convertToQuartzHealth(protocolHealth) {
|
|
22
|
+
if (protocolHealth <= 0)
|
|
23
|
+
return 0;
|
|
24
|
+
if (protocolHealth >= 100)
|
|
25
|
+
return 100;
|
|
26
|
+
return Math.floor(Math.min(100, Math.max(0, (protocolHealth - QUARTZ_HEALTH_BUFFER) / (1 - QUARTZ_HEALTH_BUFFER))));
|
|
27
|
+
}
|
|
28
|
+
getHealth() {
|
|
29
|
+
const driftHealth = this.driftUser.getHealth();
|
|
30
|
+
return this.convertToQuartzHealth(driftHealth);
|
|
31
|
+
}
|
|
32
|
+
getRepayAmountForTargetHealth(targetHealth, repayCollateralWeight) {
|
|
33
|
+
// New Quartz health after repayment is given as:
|
|
34
|
+
//
|
|
35
|
+
// loanValue - repayAmount
|
|
36
|
+
// 1 - ----------------------------------------------------------------- - quartzHealthBuffer
|
|
37
|
+
// currentWeightedCollateral - (repayAmount * repayCollateralWeight)
|
|
38
|
+
// targetHealth = -------------------------------------------------------------------------------------------
|
|
39
|
+
// 1 - quartzHealthBuffer
|
|
40
|
+
//
|
|
41
|
+
// The following is an algebraicly simplified expression of the above formula, in terms of repayAmount
|
|
42
|
+
if (targetHealth <= 0 || targetHealth >= 100)
|
|
43
|
+
throw Error(`Target health must be between 0 and 100`);
|
|
44
|
+
if (targetHealth <= this.getHealth())
|
|
45
|
+
throw Error(`Target health must be greater than current health`);
|
|
46
|
+
const currentWeightedCollateral = this.getTotalWeightedCollateral();
|
|
47
|
+
const loanValue = this.getMaintenanceMarginRequirement();
|
|
48
|
+
return Math.round((loanValue - currentWeightedCollateral * (1 - QUARTZ_HEALTH_BUFFER) * (1 - targetHealth)) / (1 - repayCollateralWeight * (1 - QUARTZ_HEALTH_BUFFER) * (1 - targetHealth)));
|
|
49
|
+
}
|
|
50
|
+
getTotalWeightedCollateral() {
|
|
51
|
+
return this.driftUser.getTotalCollateral('Maintenance').toNumber();
|
|
52
|
+
}
|
|
53
|
+
getMaintenanceMarginRequirement() {
|
|
54
|
+
return this.driftUser.getMaintenanceMarginRequirement().toNumber();
|
|
55
|
+
}
|
|
56
|
+
async makeCollateralRepayIxs(caller, callerDepositSpl, callerWithdrawSpl, callerStartingDepositBalance, jupiterExactOutRouteQuote) {
|
|
57
|
+
const depositMint = USDC_MINT;
|
|
58
|
+
const depositMarketIndex = DRIFT_MARKET_INDEX_USDC;
|
|
59
|
+
const withdrawMint = WSOL_MINT;
|
|
60
|
+
const withdrawMarketIndex = DRIFT_MARKET_INDEX_SOL;
|
|
61
|
+
if (jupiterExactOutRouteQuote.swapMode !== SwapMode.ExactOut)
|
|
62
|
+
throw Error("Jupiter quote must be ExactOutRoute");
|
|
63
|
+
if (jupiterExactOutRouteQuote.inputMint !== withdrawMint.toBase58())
|
|
64
|
+
throw Error("Jupiter quote inputMint does not match withdrawMint");
|
|
65
|
+
if (jupiterExactOutRouteQuote.outputMint !== depositMint.toBase58())
|
|
66
|
+
throw Error("Jupiter quote outputMint does not match depositMint");
|
|
67
|
+
const driftState = getDriftStatePublicKey();
|
|
68
|
+
const driftSpotMarketDeposit = getDriftSpotMarketPublicKey(depositMarketIndex);
|
|
69
|
+
const driftSpotMarketWithdraw = getDriftSpotMarketPublicKey(withdrawMarketIndex);
|
|
70
|
+
const autoRepayStartPromise = this.program.methods
|
|
71
|
+
.autoRepayStart(new BN(callerStartingDepositBalance))
|
|
72
|
+
.accounts({
|
|
73
|
+
caller: caller,
|
|
74
|
+
callerWithdrawSpl: callerWithdrawSpl,
|
|
75
|
+
withdrawMint: withdrawMint,
|
|
76
|
+
vault: this.vaultPubkey,
|
|
77
|
+
vaultWithdrawSpl: getVaultSplPublicKey(this.vaultPubkey, withdrawMint),
|
|
78
|
+
owner: this.pubkey,
|
|
79
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
80
|
+
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
81
|
+
systemProgram: SystemProgram.programId,
|
|
82
|
+
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
83
|
+
})
|
|
84
|
+
.instruction();
|
|
85
|
+
const jupiterSwapPromise = getJupiterSwapIx(caller, this.connection, jupiterExactOutRouteQuote);
|
|
86
|
+
const autoRepayDepositPromise = this.program.methods
|
|
87
|
+
.autoRepayDeposit(depositMarketIndex)
|
|
88
|
+
.accounts({
|
|
89
|
+
vault: this.vaultPubkey,
|
|
90
|
+
vaultSpl: getVaultSplPublicKey(this.vaultPubkey, depositMint),
|
|
91
|
+
owner: this.pubkey,
|
|
92
|
+
caller: caller,
|
|
93
|
+
callerSpl: callerDepositSpl,
|
|
94
|
+
splMint: depositMint,
|
|
95
|
+
driftUser: this.driftUser.pubkey,
|
|
96
|
+
driftUserStats: this.driftUser.statsPubkey,
|
|
97
|
+
driftState: driftState,
|
|
98
|
+
spotMarketVault: driftSpotMarketDeposit,
|
|
99
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
100
|
+
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
101
|
+
driftProgram: new PublicKey(DRIFT_PROGRAM_ID),
|
|
102
|
+
systemProgram: SystemProgram.programId,
|
|
103
|
+
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
104
|
+
})
|
|
105
|
+
.remainingAccounts(this.driftClient.getRemainingAccounts({
|
|
106
|
+
userAccounts: [this.driftUser.getDriftUserAccount()],
|
|
107
|
+
useMarketLastSlotCache: true,
|
|
108
|
+
writableSpotMarketIndexes: [depositMarketIndex],
|
|
109
|
+
}))
|
|
110
|
+
.instruction();
|
|
111
|
+
const autoRepayWithdrawPromise = this.program.methods
|
|
112
|
+
.autoRepayWithdraw(withdrawMarketIndex)
|
|
113
|
+
.accounts({
|
|
114
|
+
vault: this.vaultPubkey,
|
|
115
|
+
vaultSpl: getVaultSplPublicKey(this.vaultPubkey, withdrawMint),
|
|
116
|
+
owner: this.pubkey,
|
|
117
|
+
caller: caller,
|
|
118
|
+
callerSpl: callerWithdrawSpl,
|
|
119
|
+
splMint: withdrawMint,
|
|
120
|
+
driftUser: this.driftUser.pubkey,
|
|
121
|
+
driftUserStats: this.driftUser.statsPubkey,
|
|
122
|
+
driftState: driftState,
|
|
123
|
+
spotMarketVault: driftSpotMarketWithdraw,
|
|
124
|
+
driftSigner: this.driftClient.getSignerPublicKey(),
|
|
125
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
126
|
+
driftProgram: DRIFT_PROGRAM_ID,
|
|
127
|
+
systemProgram: SystemProgram.programId,
|
|
128
|
+
depositPriceUpdate: this.oracles.get("USDC/USD"),
|
|
129
|
+
withdrawPriceUpdate: this.oracles.get("SOL/USD"),
|
|
130
|
+
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
131
|
+
})
|
|
132
|
+
.remainingAccounts(this.driftClient.getRemainingAccounts({
|
|
133
|
+
userAccounts: [this.driftUser.getDriftUserAccount()],
|
|
134
|
+
useMarketLastSlotCache: true,
|
|
135
|
+
writableSpotMarketIndexes: [withdrawMarketIndex],
|
|
136
|
+
readableSpotMarketIndexes: [DRIFT_MARKET_INDEX_USDC], // Quote is in USDC
|
|
137
|
+
}))
|
|
138
|
+
.instruction();
|
|
139
|
+
const [ix_autoRepayStart, { ix_jupiterSwap, jupiterLookupTables }, ix_autoRepayDeposit, ix_autoRepayWithdraw] = await Promise.all([
|
|
140
|
+
autoRepayStartPromise,
|
|
141
|
+
jupiterSwapPromise,
|
|
142
|
+
autoRepayDepositPromise,
|
|
143
|
+
autoRepayWithdrawPromise
|
|
144
|
+
]);
|
|
145
|
+
return {
|
|
146
|
+
ixs: [
|
|
147
|
+
ix_autoRepayStart,
|
|
148
|
+
ix_jupiterSwap,
|
|
149
|
+
ix_autoRepayDeposit,
|
|
150
|
+
ix_autoRepayWithdraw
|
|
151
|
+
],
|
|
152
|
+
lookupTables: [
|
|
153
|
+
this.quartzLookupTable,
|
|
154
|
+
...jupiterLookupTables
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
export declare const getVaultPublicKey: (user: PublicKey) => PublicKey;
|
|
3
|
+
export declare const getVaultSplPublicKey: (user: PublicKey, mint: PublicKey) => PublicKey;
|
|
4
|
+
export declare const getDriftUserPublicKey: (vaultPda: PublicKey) => PublicKey;
|
|
5
|
+
export declare const getDriftUserStatsPublicKey: (vaultPda: PublicKey) => PublicKey;
|
|
6
|
+
export declare const getDriftStatePublicKey: () => PublicKey;
|
|
7
|
+
export declare const getDriftSpotMarketPublicKey: (marketIndex: number) => PublicKey;
|
|
8
|
+
export declare const toRemainingAccount: (pubkey: PublicKey, isWritable: boolean, isSigner: boolean) => {
|
|
9
|
+
pubkey: PublicKey;
|
|
10
|
+
isWritable: boolean;
|
|
11
|
+
isSigner: boolean;
|
|
12
|
+
};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { PublicKey } from "@solana/web3.js";
|
|
2
|
-
import { QUARTZ_PROGRAM_ID } from "
|
|
2
|
+
import { QUARTZ_PROGRAM_ID } from "../config/constants";
|
|
3
3
|
import { BN } from "@coral-xyz/anchor";
|
|
4
4
|
import { DRIFT_PROGRAM_ID } from "@drift-labs/sdk";
|
|
5
|
-
export const
|
|
5
|
+
export const getVaultPublicKey = (user) => {
|
|
6
6
|
const [vaultPda] = PublicKey.findProgramAddressSync([Buffer.from("vault"), user.toBuffer()], QUARTZ_PROGRAM_ID);
|
|
7
7
|
return vaultPda;
|
|
8
8
|
};
|
|
9
|
-
export const
|
|
10
|
-
const vaultPda =
|
|
9
|
+
export const getVaultSplPublicKey = (user, mint) => {
|
|
10
|
+
const vaultPda = getVaultPublicKey(user);
|
|
11
11
|
const [vaultSplPda] = PublicKey.findProgramAddressSync([vaultPda.toBuffer(), mint.toBuffer()], QUARTZ_PROGRAM_ID);
|
|
12
12
|
return vaultSplPda;
|
|
13
13
|
};
|
|
14
|
-
export const
|
|
14
|
+
export const getDriftUserPublicKey = (vaultPda) => {
|
|
15
15
|
const [userPda] = PublicKey.findProgramAddressSync([
|
|
16
16
|
Buffer.from("user"),
|
|
17
17
|
vaultPda.toBuffer(),
|
|
@@ -19,15 +19,15 @@ export const getDriftUser = (vaultPda) => {
|
|
|
19
19
|
], new PublicKey(DRIFT_PROGRAM_ID));
|
|
20
20
|
return userPda;
|
|
21
21
|
};
|
|
22
|
-
export const
|
|
22
|
+
export const getDriftUserStatsPublicKey = (vaultPda) => {
|
|
23
23
|
const [userStatsPda] = PublicKey.findProgramAddressSync([Buffer.from("user_stats"), vaultPda.toBuffer()], new PublicKey(DRIFT_PROGRAM_ID));
|
|
24
24
|
return userStatsPda;
|
|
25
25
|
};
|
|
26
|
-
export const
|
|
26
|
+
export const getDriftStatePublicKey = () => {
|
|
27
27
|
const [statePda] = PublicKey.findProgramAddressSync([Buffer.from("drift_state")], new PublicKey(DRIFT_PROGRAM_ID));
|
|
28
28
|
return statePda;
|
|
29
29
|
};
|
|
30
|
-
export const
|
|
30
|
+
export const getDriftSpotMarketPublicKey = (marketIndex) => {
|
|
31
31
|
const [spotMarketVaultPda] = PublicKey.findProgramAddressSync([
|
|
32
32
|
Buffer.from("spot_market_vault"),
|
|
33
33
|
new BN(marketIndex).toArrayLike(Buffer, 'le', 2)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PublicKey, Connection, TransactionInstruction } from "@solana/web3.js";
|
|
2
|
+
import { QuoteResponse } from "@jup-ag/api";
|
|
3
|
+
import { AddressLookupTableAccount } from "@solana/web3.js";
|
|
4
|
+
export declare function getJupiterSwapIx(walletPubkey: PublicKey, connection: Connection, quoteResponse: QuoteResponse): Promise<{
|
|
5
|
+
ix_jupiterSwap: TransactionInstruction;
|
|
6
|
+
jupiterLookupTables: AddressLookupTableAccount[];
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { PublicKey, TransactionInstruction } from "@solana/web3.js";
|
|
2
|
+
import { AddressLookupTableAccount } from "@solana/web3.js";
|
|
3
|
+
export async function getJupiterSwapIx(walletPubkey, connection, quoteResponse) {
|
|
4
|
+
const instructions = await (await fetch('https://quote-api.jup.ag/v6/swap-instructions', {
|
|
5
|
+
method: 'POST',
|
|
6
|
+
headers: {
|
|
7
|
+
'Content-Type': 'application/json'
|
|
8
|
+
},
|
|
9
|
+
body: JSON.stringify({
|
|
10
|
+
quoteResponse,
|
|
11
|
+
userPublicKey: walletPubkey.toBase58(),
|
|
12
|
+
useCompression: true,
|
|
13
|
+
})
|
|
14
|
+
})).json();
|
|
15
|
+
if (instructions.error) {
|
|
16
|
+
throw new Error("Failed to get swap instructions: " + instructions.error);
|
|
17
|
+
}
|
|
18
|
+
const { swapInstruction, addressLookupTableAddresses } = instructions;
|
|
19
|
+
const getAddressLookupTableAccounts = async (keys) => {
|
|
20
|
+
const addressLookupTableAccountInfos = await connection.getMultipleAccountsInfo(keys.map((key) => new PublicKey(key)));
|
|
21
|
+
return addressLookupTableAccountInfos.reduce((acc, accountInfo, index) => {
|
|
22
|
+
const addressLookupTableAddress = keys[index];
|
|
23
|
+
if (accountInfo) {
|
|
24
|
+
const addressLookupTableAccount = new AddressLookupTableAccount({
|
|
25
|
+
key: new PublicKey(addressLookupTableAddress),
|
|
26
|
+
state: AddressLookupTableAccount.deserialize(accountInfo.data),
|
|
27
|
+
});
|
|
28
|
+
acc.push(addressLookupTableAccount);
|
|
29
|
+
}
|
|
30
|
+
return acc;
|
|
31
|
+
}, new Array());
|
|
32
|
+
};
|
|
33
|
+
const addressLookupTableAccounts = [];
|
|
34
|
+
addressLookupTableAccounts.push(...(await getAddressLookupTableAccounts(addressLookupTableAddresses)));
|
|
35
|
+
const ix_jupiterSwap = new TransactionInstruction({
|
|
36
|
+
programId: new PublicKey(swapInstruction.programId),
|
|
37
|
+
keys: swapInstruction.accounts.map((key) => ({
|
|
38
|
+
pubkey: new PublicKey(key.pubkey),
|
|
39
|
+
isSigner: key.isSigner,
|
|
40
|
+
isWritable: key.isWritable,
|
|
41
|
+
})),
|
|
42
|
+
data: Buffer.from(swapInstruction.data, "base64"),
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
ix_jupiterSwap,
|
|
46
|
+
jupiterLookupTables: addressLookupTableAccounts,
|
|
47
|
+
};
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quartz-labs/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "SDK for interacting with the Quartz Protocol",
|
|
5
|
-
"main": "
|
|
6
|
-
"types": "
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
7
10
|
"typesVersions": {
|
|
8
11
|
">=4.7": {
|
|
9
12
|
"*": [
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { PublicKey } from "@solana/web3.js";
|
|
2
|
-
export const QUARTZ_PROGRAM_ID = new PublicKey("6JjHXLheGSNvvexgzMthEcgjkcirDrGduc3HAKB2P1v2");
|
|
3
|
-
export const QUARTZ_ADDRESS_TABLE = new PublicKey("96BmeKKVGX3LKYSKo3FCEom1YpNY11kCnGscKq6ouxLx");
|
|
4
|
-
export const QUARTZ_HEALTH_BUFFER = 0.1;
|
|
5
|
-
export const DRIFT_MARKET_INDEX_USDC = 0;
|
|
6
|
-
export const DRIFT_MARKET_INDEX_SOL = 1;
|
|
7
|
-
export const SUPPORTED_DRIFT_MARKETS = [DRIFT_MARKET_INDEX_USDC, DRIFT_MARKET_INDEX_SOL];
|
package/build/helpers.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { PublicKey } from "@solana/web3.js";
|
|
2
|
-
export declare const getVaultPubkey: (user: PublicKey) => PublicKey;
|
|
3
|
-
export declare const getVaultSplPubkey: (user: PublicKey, mint: PublicKey) => PublicKey;
|
|
4
|
-
export declare const getDriftUser: (vaultPda: PublicKey) => PublicKey;
|
|
5
|
-
export declare const getDriftUserStats: (vaultPda: PublicKey) => PublicKey;
|
|
6
|
-
export declare const getDriftState: () => PublicKey;
|
|
7
|
-
export declare const getDriftSpotMarketVault: (marketIndex: number) => PublicKey;
|
|
8
|
-
export declare const toRemainingAccount: (pubkey: PublicKey, isWritable: boolean, isSigner: boolean) => {
|
|
9
|
-
pubkey: PublicKey;
|
|
10
|
-
isWritable: boolean;
|
|
11
|
-
isSigner: boolean;
|
|
12
|
-
};
|
package/build/index.d.ts
DELETED
package/build/user.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { PublicKey } from "@solana/web3.js";
|
|
2
|
-
import { DriftClient, UserAccount } from "@drift-labs/sdk";
|
|
3
|
-
import { Connection } from "@solana/web3.js";
|
|
4
|
-
export declare class QuartzUser {
|
|
5
|
-
private pubkey;
|
|
6
|
-
private vaultPubkey;
|
|
7
|
-
private connection;
|
|
8
|
-
private driftClient;
|
|
9
|
-
private driftUser;
|
|
10
|
-
constructor(pubkey: PublicKey, connection: Connection, driftClient: DriftClient, driftUserAccount?: UserAccount);
|
|
11
|
-
private convertToQuartzHealth;
|
|
12
|
-
getHealth(): number;
|
|
13
|
-
getRepayAmountForTargetHealth(targetHealth: number, repayCollateralWeight: number): number;
|
|
14
|
-
getTotalWeightedCollateral(): number;
|
|
15
|
-
getMaintenanceMarginRequirement(): number;
|
|
16
|
-
}
|
package/build/user.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { DriftUser } from "./model/driftUser";
|
|
2
|
-
import { getVaultPubkey } from "./helpers";
|
|
3
|
-
import { QUARTZ_HEALTH_BUFFER } from "./config/constants";
|
|
4
|
-
export class QuartzUser {
|
|
5
|
-
constructor(pubkey, connection, driftClient, driftUserAccount) {
|
|
6
|
-
this.pubkey = pubkey;
|
|
7
|
-
this.vaultPubkey = getVaultPubkey(pubkey);
|
|
8
|
-
this.connection = connection;
|
|
9
|
-
this.driftClient = driftClient;
|
|
10
|
-
this.driftUser = new DriftUser(this.vaultPubkey, connection, driftClient, driftUserAccount);
|
|
11
|
-
}
|
|
12
|
-
convertToQuartzHealth(protocolHealth) {
|
|
13
|
-
if (protocolHealth <= 0)
|
|
14
|
-
return 0;
|
|
15
|
-
if (protocolHealth >= 100)
|
|
16
|
-
return 100;
|
|
17
|
-
return Math.floor(Math.min(100, Math.max(0, (protocolHealth - QUARTZ_HEALTH_BUFFER) / (1 - QUARTZ_HEALTH_BUFFER))));
|
|
18
|
-
}
|
|
19
|
-
getHealth() {
|
|
20
|
-
const driftHealth = this.driftUser.getHealth();
|
|
21
|
-
return this.convertToQuartzHealth(driftHealth);
|
|
22
|
-
}
|
|
23
|
-
getRepayAmountForTargetHealth(targetHealth, repayCollateralWeight) {
|
|
24
|
-
// New Quartz health after repayment is given as:
|
|
25
|
-
//
|
|
26
|
-
// loanValue - repayAmount
|
|
27
|
-
// 1 - ----------------------------------------------------------------- - quartzHealthBuffer
|
|
28
|
-
// currentWeightedCollateral - (repayAmount * repayCollateralWeight)
|
|
29
|
-
// targetHealth = -------------------------------------------------------------------------------------------
|
|
30
|
-
// 1 - quartzHealthBuffer
|
|
31
|
-
//
|
|
32
|
-
// The following is an algebraicly simplified expression of the above formula, in terms of repayAmount
|
|
33
|
-
if (targetHealth <= 0 || targetHealth >= 100)
|
|
34
|
-
throw Error(`Target health must be between 0 and 100`);
|
|
35
|
-
if (targetHealth <= this.getHealth())
|
|
36
|
-
throw Error(`Target health must be greater than current health`);
|
|
37
|
-
const currentWeightedCollateral = this.getTotalWeightedCollateral();
|
|
38
|
-
const loanValue = this.getMaintenanceMarginRequirement();
|
|
39
|
-
return Math.round((loanValue - currentWeightedCollateral * (1 - QUARTZ_HEALTH_BUFFER) * (1 - targetHealth)) / (1 - repayCollateralWeight * (1 - QUARTZ_HEALTH_BUFFER) * (1 - targetHealth)));
|
|
40
|
-
}
|
|
41
|
-
getTotalWeightedCollateral() {
|
|
42
|
-
return this.driftUser.getTotalCollateral('Maintenance').toNumber();
|
|
43
|
-
}
|
|
44
|
-
getMaintenanceMarginRequirement() {
|
|
45
|
-
return this.driftUser.getMaintenanceMarginRequirement().toNumber();
|
|
46
|
-
}
|
|
47
|
-
}
|
package/jest.config.js
DELETED