Skip to content

Commit

Permalink
program: add ability to customize modify order policy (#461)
Browse files Browse the repository at this point in the history
* make sure modifyParams have undefined values set as null

* add modify order policy

* make policy optional

* update sdk

* cancel order wont fail if order doesn't exist

* CHANGELOG
  • Loading branch information
crispheaney committed May 12, 2023
1 parent 84b45e2 commit 116972e
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 93 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Breaking

- program: modify_order and modify_order_by_id now expect a ModifyOrderPolicy ([#461](https://github.com/drift-labs/protocol-v2/pull/461))
- program: cancel_order does not fail if order does not exist ([#461](https://github.com/drift-labs/protocol-v2/pull/461))

## [2.28.0] - 2023-05-11

### Features
Expand Down
42 changes: 29 additions & 13 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use crate::controller::spot_position::{
};
use crate::error::DriftResult;
use crate::error::ErrorCode;
use crate::get_then_update_id;
use crate::instructions::OrderParams;
use crate::load_mut;
use crate::math::auction::calculate_auction_prices;
Expand All @@ -48,6 +47,7 @@ use crate::math::stats::calculate_new_twap;
use crate::math::{amm, fees, margin::*, orders::*};
use crate::{controller, PostOnlyParam};
use crate::{get_struct_values, ModifyOrderParams};
use crate::{get_then_update_id, ModifyOrderPolicy};

use crate::math::amm::calculate_amm_available_liquidity;
use crate::math::safe_unwrap::SafeUnwrap;
Expand Down Expand Up @@ -494,7 +494,7 @@ pub fn cancel_order_by_order_id(
Ok(order_index) => order_index,
Err(_) => {
msg!("could not find order id {}", order_id);
return Err(ErrorCode::InvalidOrder);
return Ok(());
}
};

Expand Down Expand Up @@ -536,7 +536,7 @@ pub fn cancel_order_by_user_order_id(
Some(order_index) => order_index,
None => {
msg!("could not find user order id {}", user_order_id);
return Err(ErrorCode::InvalidOrder);
return Ok(());
}
};

Expand Down Expand Up @@ -677,18 +677,34 @@ pub fn modify_order(
) -> DriftResult {
let user_key = user_loader.key();
let mut user = load_mut!(user_loader)?;

let order_index = match order_id {
ModifyOrderId::UserOrderId(user_order_id) => user
.get_order_index_by_user_order_id(user_order_id)
.map_err(|e| {
msg!("User order id {} not found", user_order_id);
e
})?,
ModifyOrderId::OrderId(order_id) => user.get_order_index(order_id).map_err(|e| {
msg!("Order id {} not found", order_id);
e
})?,
ModifyOrderId::UserOrderId(user_order_id) => {
match user.get_order_index_by_user_order_id(user_order_id) {
Ok(order_index) => order_index,
Err(e) => {
msg!("User order id {} not found", user_order_id);
if modify_order_params.policy == Some(ModifyOrderPolicy::MustModify) {
return Err(e);
} else {
return Ok(());
}
}
}
}
ModifyOrderId::OrderId(order_id) => match user.get_order_index(order_id) {
Ok(order_index) => order_index,
Err(e) => {
msg!("Order id {} not found", order_id);
if modify_order_params.policy == Some(ModifyOrderPolicy::MustModify) {
return Err(e);
} else {
return Ok(());
}
}
},
};

let existing_order = user.orders[order_index];

cancel_order(
Expand Down
13 changes: 13 additions & 0 deletions programs/drift/src/instructions/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,19 @@ pub struct ModifyOrderParams {
pub auction_duration: Option<u8>,
pub auction_start_price: Option<i64>,
pub auction_end_price: Option<i64>,
pub policy: Option<ModifyOrderPolicy>,
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Eq, PartialEq)]
pub enum ModifyOrderPolicy {
TryModify,
MustModify,
}

impl Default for ModifyOrderPolicy {
fn default() -> Self {
Self::TryModify
}
}

#[access_control(
Expand Down
191 changes: 114 additions & 77 deletions sdk/src/driftClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3807,22 +3807,54 @@ export class DriftClient {

/**
* Modifies an open order (spot or perp) by closing it and replacing it with a new order.
* @param orderId: The open order to modify
* @param newDirection: The new direction for the order
* @param newBaseAmount: The new base amount for the order
* @param newLimitPice: The new limit price for the order
* @param newOraclePriceOffset: The new oracle price offset for the order
* @param newTriggerPrice: Optional - Thew new trigger price for the order.
* @param auctionDuration:
* @param auctionStartPrice:
* @param auctionEndPrice:
* @param reduceOnly:
* @param postOnly:
* @param immediateOrCancel:
* @param maxTs:
* @param orderParams.orderId: The open order to modify
* @param orderParams.newDirection: The new direction for the order
* @param orderParams.newBaseAmount: The new base amount for the order
* @param orderParams.newLimitPice: The new limit price for the order
* @param orderParams.newOraclePriceOffset: The new oracle price offset for the order
* @param orderParams.newTriggerPrice: Optional - Thew new trigger price for the order.
* @param orderParams.auctionDuration:
* @param orderParams.auctionStartPrice:
* @param orderParams.auctionEndPrice:
* @param orderParams.reduceOnly:
* @param orderParams.postOnly:
* @param orderParams.immediateOrCancel:
* @param orderParams.policy:
* @param orderParams.maxTs:
* @returns
*/
public async modifyOrder({
public async modifyOrder(
orderParams: {
orderId: number;
newDirection?: PositionDirection;
newBaseAmount?: BN;
newLimitPrice?: BN;
newOraclePriceOffset?: number;
newTriggerPrice?: BN;
newTriggerCondition?: OrderTriggerCondition;
auctionDuration?: number;
auctionStartPrice?: BN;
auctionEndPrice?: BN;
reduceOnly?: boolean;
postOnly?: boolean;
immediateOrCancel?: boolean;
maxTs?: BN;
policy?: ModifyOrderParams;
},
txParams?: TxParams
): Promise<TransactionSignature> {
const { txSig } = await this.sendTransaction(
await this.buildTransaction(
await this.getModifyOrderIx(orderParams),
txParams
),
[],
this.opts
);
return txSig;
}

public async getModifyOrderIx({
orderId,
newDirection,
newBaseAmount,
Expand All @@ -3837,7 +3869,7 @@ export class DriftClient {
postOnly,
immediateOrCancel,
maxTs,
txParams,
policy,
}: {
orderId: number;
newDirection?: PositionDirection;
Expand All @@ -3853,8 +3885,15 @@ export class DriftClient {
postOnly?: boolean;
immediateOrCancel?: boolean;
maxTs?: BN;
txParams?: TxParams;
}): Promise<TransactionSignature> {
policy?: ModifyOrderParams;
}): Promise<TransactionInstruction> {
const userAccountPublicKey = await this.getUserAccountPublicKey();

const remainingAccounts = this.getRemainingAccounts({
userAccounts: [this.getUserAccount()],
useMarketLastSlotCache: true,
});

const orderParams: ModifyOrderParams = {
baseAssetAmount: newBaseAmount || null,
direction: newDirection || null,
Expand All @@ -3868,31 +3907,10 @@ export class DriftClient {
reduceOnly: reduceOnly || null,
postOnly: postOnly || null,
immediateOrCancel: immediateOrCancel || null,
policy: policy || null,
maxTs: maxTs || null,
};

const { txSig } = await this.sendTransaction(
await this.buildTransaction(
await this.getModifyOrderIx(orderId, orderParams),
txParams
),
[],
this.opts
);
return txSig;
}

public async getModifyOrderIx(
orderId: number,
orderParams: ModifyOrderParams
): Promise<TransactionInstruction> {
const userAccountPublicKey = await this.getUserAccountPublicKey();

const remainingAccounts = this.getRemainingAccounts({
userAccounts: [this.getUserAccount()],
useMarketLastSlotCache: true,
});

return await this.program.instruction.modifyOrder(orderId, orderParams, {
accounts: {
state: await this.getStatePublicKey(),
Expand All @@ -3906,22 +3924,54 @@ export class DriftClient {

/**
* Modifies an open order by closing it and replacing it with a new order.
* @param userOrderId: The open order to modify
* @param newDirection: The new direction for the order
* @param newBaseAmount: The new base amount for the order
* @param newLimitPice: The new limit price for the order
* @param newOraclePriceOffset: The new oracle price offset for the order
* @param newTriggerPrice: Optional - Thew new trigger price for the order.
* @param auctionDuration: Only required if order type changed to market from something else
* @param auctionStartPrice: Only required if order type changed to market from something else
* @param auctionEndPrice: Only required if order type changed to market from something else
* @param reduceOnly:
* @param postOnly:
* @param immediateOrCancel:
* @param maxTs:
* @param orderParams.userOrderId: The open order to modify
* @param orderParams.newDirection: The new direction for the order
* @param orderParams.newBaseAmount: The new base amount for the order
* @param orderParams.newLimitPice: The new limit price for the order
* @param orderParams.newOraclePriceOffset: The new oracle price offset for the order
* @param orderParams.newTriggerPrice: Optional - Thew new trigger price for the order.
* @param orderParams.auctionDuration: Only required if order type changed to market from something else
* @param orderParams.auctionStartPrice: Only required if order type changed to market from something else
* @param orderParams.auctionEndPrice: Only required if order type changed to market from something else
* @param orderParams.reduceOnly:
* @param orderParams.postOnly:
* @param orderParams.immediateOrCancel:
* @param orderParams.policy:
* @param orderParams.maxTs:
* @returns
*/
public async modifyOrderByUserOrderId({
public async modifyOrderByUserOrderId(
orderParams: {
userOrderId: number;
newDirection?: PositionDirection;
newBaseAmount?: BN;
newLimitPrice?: BN;
newOraclePriceOffset?: number;
newTriggerPrice?: BN;
newTriggerCondition?: OrderTriggerCondition;
auctionDuration?: number;
auctionStartPrice?: BN;
auctionEndPrice?: BN;
reduceOnly?: boolean;
postOnly?: boolean;
immediateOrCancel?: boolean;
policy?: ModifyOrderParams;
maxTs?: BN;
},
txParams?: TxParams
): Promise<TransactionSignature> {
const { txSig } = await this.sendTransaction(
await this.buildTransaction(
await this.getModifyOrderByUserIdIx(orderParams),
txParams
),
[],
this.opts
);
return txSig;
}

public async getModifyOrderByUserIdIx({
userOrderId,
newDirection,
newBaseAmount,
Expand All @@ -3936,7 +3986,7 @@ export class DriftClient {
postOnly,
immediateOrCancel,
maxTs,
txParams,
policy,
}: {
userOrderId: number;
newDirection?: PositionDirection;
Expand All @@ -3951,9 +4001,17 @@ export class DriftClient {
reduceOnly?: boolean;
postOnly?: boolean;
immediateOrCancel?: boolean;
policy?: ModifyOrderParams;
maxTs?: BN;
txParams?: TxParams;
}): Promise<TransactionSignature> {
}): Promise<TransactionInstruction> {
const userAccountPublicKey = await this.getUserAccountPublicKey();

const remainingAccounts = this.getRemainingAccounts({
userAccounts: [this.getUserAccount()],
useMarketLastSlotCache: true,
});

const orderParams: ModifyOrderParams = {
baseAssetAmount: newBaseAmount || null,
direction: newDirection || null,
Expand All @@ -3967,31 +4025,10 @@ export class DriftClient {
reduceOnly: reduceOnly || null,
postOnly: postOnly || null,
immediateOrCancel: immediateOrCancel || null,
policy: policy || null,
maxTs: maxTs || null,
};

const { txSig } = await this.sendTransaction(
await this.buildTransaction(
await this.getModifyOrderByUserIdIx(userOrderId, orderParams),
txParams
),
[],
this.opts
);
return txSig;
}

public async getModifyOrderByUserIdIx(
userOrderId: number,
orderParams: ModifyOrderParams
): Promise<TransactionInstruction> {
const userAccountPublicKey = await this.getUserAccountPublicKey();

const remainingAccounts = this.getRemainingAccounts({
userAccounts: [this.getUserAccount()],
useMarketLastSlotCache: true,
});

return await this.program.instruction.modifyOrderByUserId(
userOrderId,
orderParams,
Expand Down
Loading

0 comments on commit 116972e

Please sign in to comment.