Skip to content

Commit 1fd7490

Browse files
feat: add margin ratio ix to open orders + swift prop (#1864)
* feat: add margin ratio ix to open orders + swift prop * fix: bug with max lev available calculation * fix: bug with swift msg encoding + margin ratio * feat: re-add types for swift non-optional * rm: unneeded undefined check on swift maxMarginRation * allow enter HLM on position margin ratio update * fix margin ratio calc * updates * rm logs --------- Co-authored-by: Nick Caradonna <[email protected]>
1 parent aedb8d6 commit 1fd7490

File tree

3 files changed

+139
-27
lines changed

3 files changed

+139
-27
lines changed

sdk/src/driftClient.ts

Lines changed: 102 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ import { TxSender, TxSigAndSlot } from './tx/types';
126126
import {
127127
BASE_PRECISION,
128128
GOV_SPOT_MARKET_INDEX,
129+
MARGIN_PRECISION,
129130
ONE,
130131
PERCENTAGE_PRECISION,
131132
PRICE_PRECISION,
@@ -1723,11 +1724,11 @@ export class DriftClient {
17231724
): Promise<TransactionInstruction> {
17241725
const userAccountPublicKey = getUserAccountPublicKeySync(
17251726
this.program.programId,
1726-
this.wallet.publicKey,
1727+
this.authority,
17271728
subAccountId
17281729
);
17291730

1730-
await this.addUser(subAccountId, this.wallet.publicKey);
1731+
await this.addUser(subAccountId, this.authority);
17311732

17321733
const ix = this.program.instruction.updateUserPerpPositionCustomMarginRatio(
17331734
subAccountId,
@@ -1748,14 +1749,21 @@ export class DriftClient {
17481749
perpMarketIndex: number,
17491750
marginRatio: number,
17501751
subAccountId = 0,
1751-
txParams?: TxParams
1752+
txParams?: TxParams,
1753+
enterHighLeverageMode?: boolean
17521754
): Promise<TransactionSignature> {
1753-
const ix = await this.getUpdateUserPerpPositionCustomMarginRatioIx(
1755+
const ixs = [];
1756+
if (enterHighLeverageMode) {
1757+
const enableIx = await this.getEnableHighLeverageModeIx(subAccountId);
1758+
ixs.push(enableIx);
1759+
}
1760+
const updateIx = await this.getUpdateUserPerpPositionCustomMarginRatioIx(
17541761
perpMarketIndex,
17551762
marginRatio,
17561763
subAccountId
17571764
);
1758-
const tx = await this.buildTransaction(ix, txParams ?? this.txParams);
1765+
ixs.push(updateIx);
1766+
const tx = await this.buildTransaction(ixs, txParams ?? this.txParams);
17591767
const { txSig } = await this.sendTransaction(tx, [], this.opts);
17601768
return txSig;
17611769
}
@@ -4292,7 +4300,8 @@ export class DriftClient {
42924300
bracketOrdersParams = new Array<OptionalOrderParams>(),
42934301
referrerInfo?: ReferrerInfo,
42944302
cancelExistingOrders?: boolean,
4295-
settlePnl?: boolean
4303+
settlePnl?: boolean,
4304+
positionMaxLev?: number
42964305
): Promise<{
42974306
cancelExistingOrdersTx?: Transaction | VersionedTransaction;
42984307
settlePnlTx?: Transaction | VersionedTransaction;
@@ -4308,7 +4317,10 @@ export class DriftClient {
43084317
const marketIndex = orderParams.marketIndex;
43094318
const orderId = userAccount.nextOrderId;
43104319

4311-
const ixPromisesForTxs: Record<TxKeys, Promise<TransactionInstruction>> = {
4320+
const ixPromisesForTxs: Record<
4321+
TxKeys,
4322+
Promise<TransactionInstruction | TransactionInstruction[]>
4323+
> = {
43124324
cancelExistingOrdersTx: undefined,
43134325
settlePnlTx: undefined,
43144326
fillTx: undefined,
@@ -4317,10 +4329,18 @@ export class DriftClient {
43174329

43184330
const txKeys = Object.keys(ixPromisesForTxs);
43194331

4320-
ixPromisesForTxs.marketOrderTx = this.getPlaceOrdersIx(
4321-
[orderParams, ...bracketOrdersParams],
4322-
userAccount.subAccountId
4323-
);
4332+
const marketOrderTxIxs = positionMaxLev
4333+
? this.getPlaceOrdersAndSetPositionMaxLevIx(
4334+
[orderParams, ...bracketOrdersParams],
4335+
positionMaxLev,
4336+
userAccount.subAccountId
4337+
)
4338+
: this.getPlaceOrdersIx(
4339+
[orderParams, ...bracketOrdersParams],
4340+
userAccount.subAccountId
4341+
);
4342+
4343+
ixPromisesForTxs.marketOrderTx = marketOrderTxIxs;
43244344

43254345
/* Cancel open orders in market if requested */
43264346
if (cancelExistingOrders && isVariant(orderParams.marketType, 'perp')) {
@@ -4361,7 +4381,10 @@ export class DriftClient {
43614381
const ixsMap = ixs.reduce((acc, ix, i) => {
43624382
acc[txKeys[i]] = ix;
43634383
return acc;
4364-
}, {}) as MappedRecord<typeof ixPromisesForTxs, TransactionInstruction>;
4384+
}, {}) as MappedRecord<
4385+
typeof ixPromisesForTxs,
4386+
TransactionInstruction | TransactionInstruction[]
4387+
>;
43654388

43664389
const txsMap = (await this.buildTransactionsMap(
43674390
ixsMap,
@@ -4945,6 +4968,73 @@ export class DriftClient {
49454968
});
49464969
}
49474970

4971+
public async getPlaceOrdersAndSetPositionMaxLevIx(
4972+
params: OptionalOrderParams[],
4973+
positionMaxLev: number,
4974+
subAccountId?: number
4975+
): Promise<TransactionInstruction[]> {
4976+
const user = await this.getUserAccountPublicKey(subAccountId);
4977+
4978+
const readablePerpMarketIndex: number[] = [];
4979+
const readableSpotMarketIndexes: number[] = [];
4980+
for (const param of params) {
4981+
if (!param.marketType) {
4982+
throw new Error('must set param.marketType');
4983+
}
4984+
if (isVariant(param.marketType, 'perp')) {
4985+
readablePerpMarketIndex.push(param.marketIndex);
4986+
} else {
4987+
readableSpotMarketIndexes.push(param.marketIndex);
4988+
}
4989+
}
4990+
4991+
const remainingAccounts = this.getRemainingAccounts({
4992+
userAccounts: [this.getUserAccount(subAccountId)],
4993+
readablePerpMarketIndex,
4994+
readableSpotMarketIndexes,
4995+
useMarketLastSlotCache: true,
4996+
});
4997+
4998+
for (const param of params) {
4999+
if (isUpdateHighLeverageMode(param.bitFlags)) {
5000+
remainingAccounts.push({
5001+
pubkey: getHighLeverageModeConfigPublicKey(this.program.programId),
5002+
isWritable: true,
5003+
isSigner: false,
5004+
});
5005+
}
5006+
}
5007+
5008+
const formattedParams = params.map((item) => getOrderParams(item));
5009+
5010+
const placeOrdersIxs = await this.program.instruction.placeOrders(
5011+
formattedParams,
5012+
{
5013+
accounts: {
5014+
state: await this.getStatePublicKey(),
5015+
user,
5016+
userStats: this.getUserStatsAccountPublicKey(),
5017+
authority: this.wallet.publicKey,
5018+
},
5019+
remainingAccounts,
5020+
}
5021+
);
5022+
5023+
const marginRatio = Math.floor(
5024+
(1 / positionMaxLev) * MARGIN_PRECISION.toNumber()
5025+
);
5026+
5027+
// TODO: Handle multiple markets?
5028+
const setPositionMaxLevIxs =
5029+
await this.getUpdateUserPerpPositionCustomMarginRatioIx(
5030+
readablePerpMarketIndex[0],
5031+
marginRatio,
5032+
subAccountId
5033+
);
5034+
5035+
return [placeOrdersIxs, setPositionMaxLevIxs];
5036+
}
5037+
49485038
public async fillPerpOrder(
49495039
userAccountPublicKey: PublicKey,
49505040
user: UserAccount,

sdk/src/user.ts

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,8 @@ export class User {
449449
public getPerpBuyingPower(
450450
marketIndex: number,
451451
collateralBuffer = ZERO,
452-
enterHighLeverageMode = undefined
452+
enterHighLeverageMode = undefined,
453+
maxMarginRatio = undefined
453454
): BN {
454455
const perpPosition = this.getPerpPositionOrEmpty(marketIndex);
455456

@@ -473,7 +474,7 @@ export class User {
473474
freeCollateral,
474475
worstCaseBaseAssetAmount,
475476
enterHighLeverageMode,
476-
perpPosition
477+
maxMarginRatio || perpPosition.maxMarginRatio
477478
);
478479
}
479480

@@ -482,17 +483,17 @@ export class User {
482483
freeCollateral: BN,
483484
baseAssetAmount: BN,
484485
enterHighLeverageMode = undefined,
485-
perpPosition?: PerpPosition
486+
perpMarketMaxMarginRatio = undefined
486487
): BN {
487-
const userCustomMargin = Math.max(
488-
perpPosition?.maxMarginRatio ?? 0,
488+
const maxMarginRatio = Math.max(
489+
perpMarketMaxMarginRatio,
489490
this.getUserAccount().maxMarginRatio
490491
);
491492
const marginRatio = calculateMarketMarginRatio(
492493
this.driftClient.getPerpMarketAccount(marketIndex),
493494
baseAssetAmount,
494495
'Initial',
495-
userCustomMargin,
496+
maxMarginRatio,
496497
enterHighLeverageMode || this.isHighLeverageMode('Initial')
497498
);
498499

@@ -1247,7 +1248,10 @@ export class User {
12471248
}
12481249

12491250
if (marginCategory) {
1250-
const userCustomMargin = this.getUserAccount().maxMarginRatio;
1251+
const userCustomMargin = Math.max(
1252+
perpPosition.maxMarginRatio,
1253+
this.getUserAccount().maxMarginRatio
1254+
);
12511255
let marginRatio = new BN(
12521256
calculateMarketMarginRatio(
12531257
market,
@@ -2345,13 +2349,18 @@ export class User {
23452349
public getMarginUSDCRequiredForTrade(
23462350
targetMarketIndex: number,
23472351
baseSize: BN,
2348-
estEntryPrice?: BN
2352+
estEntryPrice?: BN,
2353+
perpMarketMaxMarginRatio?: number
23492354
): BN {
2355+
const maxMarginRatio = Math.max(
2356+
perpMarketMaxMarginRatio,
2357+
this.getUserAccount().maxMarginRatio
2358+
);
23502359
return calculateMarginUSDCRequiredForTrade(
23512360
this.driftClient,
23522361
targetMarketIndex,
23532362
baseSize,
2354-
this.getUserAccount().maxMarginRatio,
2363+
maxMarginRatio,
23552364
undefined,
23562365
estEntryPrice
23572366
);
@@ -2360,14 +2369,19 @@ export class User {
23602369
public getCollateralDepositRequiredForTrade(
23612370
targetMarketIndex: number,
23622371
baseSize: BN,
2363-
collateralIndex: number
2372+
collateralIndex: number,
2373+
perpMarketMaxMarginRatio?: number
23642374
): BN {
2375+
const maxMarginRatio = Math.max(
2376+
perpMarketMaxMarginRatio,
2377+
this.getUserAccount().maxMarginRatio
2378+
);
23652379
return calculateCollateralDepositRequiredForTrade(
23662380
this.driftClient,
23672381
targetMarketIndex,
23682382
baseSize,
23692383
collateralIndex,
2370-
this.getUserAccount().maxMarginRatio,
2384+
maxMarginRatio,
23712385
false // assume user cant be high leverage if they havent created user account ?
23722386
);
23732387
}
@@ -2385,7 +2399,8 @@ export class User {
23852399
targetMarketIndex: number,
23862400
tradeSide: PositionDirection,
23872401
isLp = false,
2388-
enterHighLeverageMode = undefined
2402+
enterHighLeverageMode = undefined,
2403+
maxMarginRatio = undefined
23892404
): { tradeSize: BN; oppositeSideTradeSize: BN } {
23902405
let tradeSize = ZERO;
23912406
let oppositeSideTradeSize = ZERO;
@@ -2424,7 +2439,8 @@ export class User {
24242439
const maxPositionSize = this.getPerpBuyingPower(
24252440
targetMarketIndex,
24262441
lpBuffer,
2427-
enterHighLeverageMode
2442+
enterHighLeverageMode,
2443+
maxMarginRatio
24282444
);
24292445

24302446
if (maxPositionSize.gte(ZERO)) {
@@ -2451,8 +2467,12 @@ export class User {
24512467
const marginRequirement = this.getInitialMarginRequirement(
24522468
enterHighLeverageMode
24532469
);
2470+
const marginRatio = Math.max(
2471+
currentPosition.maxMarginRatio,
2472+
this.getUserAccount().maxMarginRatio
2473+
);
24542474
const marginFreedByClosing = perpLiabilityValue
2455-
.mul(new BN(market.marginRatioInitial))
2475+
.mul(new BN(marginRatio))
24562476
.div(MARGIN_PRECISION);
24572477
const marginRequirementAfterClosing =
24582478
marginRequirement.sub(marginFreedByClosing);
@@ -2468,7 +2488,8 @@ export class User {
24682488
this.getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount(
24692489
targetMarketIndex,
24702490
freeCollateralAfterClose,
2471-
ZERO
2491+
ZERO,
2492+
currentPosition.maxMarginRatio
24722493
);
24732494
oppositeSideTradeSize = perpLiabilityValue;
24742495
tradeSize = buyingPowerAfterClose;

sdk/tests/dlob/helpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export const mockPerpPosition: PerpPosition = {
4444
lastBaseAssetAmountPerLp: new BN(0),
4545
lastQuoteAssetAmountPerLp: new BN(0),
4646
perLpBase: 0,
47+
maxMarginRatio: 1,
4748
};
4849

4950
export const mockAMM: AMM = {

0 commit comments

Comments
 (0)