Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f43db4a

Browse files
authoredJun 10, 2025··
program: allow all limit orders to go through swift (#1661)
* program: allow all limit orders to go through swift * add anchor test * CHANGELOG
1 parent ca95512 commit f43db4a

File tree

4 files changed

+276
-24
lines changed

4 files changed

+276
-24
lines changed
 

‎CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Features
1111

12+
- program: allow limit orders without auctions in swift ([#1661](https://github.com/drift-labs/protocol-v2/pull/1661))
13+
1214
### Fixes
1315

1416
### Breaking

‎programs/drift/src/instructions/keeper.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -724,15 +724,6 @@ pub fn place_signed_msg_taker_order<'c: 'info, 'info>(
724724
return Err(print_error!(ErrorCode::InvalidSignedMsgOrderParam)().into());
725725
}
726726

727-
// Make sure that them auction parameters are set
728-
if matching_taker_order_params.auction_duration.is_none()
729-
|| matching_taker_order_params.auction_start_price.is_none()
730-
|| matching_taker_order_params.auction_end_price.is_none()
731-
{
732-
msg!("Auction params must be set for signed msg orders");
733-
return Err(print_error!(ErrorCode::InvalidSignedMsgOrderParam)().into());
734-
}
735-
736727
// Set max slot for the order early so we set correct signed msg order id
737728
let order_slot = verified_message_and_signature.slot;
738729
if order_slot < clock.slot.saturating_sub(500) {
@@ -743,12 +734,21 @@ pub fn place_signed_msg_taker_order<'c: 'info, 'info>(
743734
return Err(print_error!(ErrorCode::InvalidSignedMsgOrderParam)().into());
744735
}
745736
let market_index = matching_taker_order_params.market_index;
746-
let max_slot = order_slot.safe_add(
747-
matching_taker_order_params
748-
.auction_duration
749-
.unwrap()
750-
.cast::<u64>()?,
751-
)?;
737+
let max_slot = if matching_taker_order_params.order_type == OrderType::Limit {
738+
order_slot.safe_add(
739+
matching_taker_order_params
740+
.auction_duration
741+
.unwrap_or(0)
742+
.cast::<u64>()?,
743+
)?
744+
} else {
745+
order_slot.safe_add(
746+
matching_taker_order_params
747+
.auction_duration
748+
.unwrap()
749+
.cast::<u64>()?,
750+
)?
751+
};
752752

753753
// Dont place order if max slot already passed
754754
if max_slot < clock.slot {

‎programs/drift/src/state/order_params.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,25 @@ pub enum OrderParamsBitFlag {
4646

4747
impl OrderParams {
4848
pub fn has_valid_auction_params(&self) -> DriftResult<bool> {
49-
if self.auction_duration.is_none()
50-
|| self.auction_start_price.is_none()
51-
|| self.auction_end_price.is_none()
49+
if self.auction_duration.is_some()
50+
&& self.auction_start_price.is_some()
51+
&& self.auction_end_price.is_some()
5252
{
53-
return Ok(false);
54-
} else {
5553
if self.direction == PositionDirection::Long {
5654
return Ok(self.auction_start_price.safe_unwrap()?
5755
<= self.auction_end_price.safe_unwrap()?);
5856
} else {
5957
return Ok(self.auction_start_price.safe_unwrap()?
6058
>= self.auction_end_price.safe_unwrap()?);
6159
}
60+
} else if self.order_type == OrderType::Limit
61+
&& self.auction_duration.is_none()
62+
&& self.auction_start_price.is_none()
63+
&& self.auction_end_price.is_none()
64+
{
65+
return Ok(true);
66+
} else {
67+
return Ok(false);
6268
}
6369
}
6470

‎tests/placeAndMakeSignedMsg.ts

Lines changed: 248 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -451,10 +451,6 @@ describe('place and make signedMsg order', () => {
451451
},
452452
});
453453
await takerDriftClientUser.subscribe();
454-
await takerDriftClient.initializeSignedMsgUserOrders(
455-
takerDriftClientUser.getUserAccount().authority,
456-
32
457-
);
458454

459455
const delegate = Keypair.generate();
460456
await takerDriftClient.updateUserDelegate(delegate.publicKey);
@@ -543,4 +539,252 @@ describe('place and make signedMsg order', () => {
543539
);
544540
}
545541
});
542+
543+
it('limit order no auction params', async () => {
544+
const keypair = new Keypair();
545+
await provider.connection.requestAirdrop(keypair.publicKey, 10 ** 9);
546+
await sleep(1000);
547+
const wallet = new Wallet(keypair);
548+
const userUSDCAccount = await mockUserUSDCAccount(
549+
usdcMint,
550+
usdcAmount,
551+
provider,
552+
keypair.publicKey
553+
);
554+
const takerDriftClient = new TestClient({
555+
connection,
556+
wallet,
557+
programID: chProgram.programId,
558+
opts: {
559+
commitment: 'confirmed',
560+
},
561+
activeSubAccountId: 0,
562+
perpMarketIndexes: marketIndexes,
563+
spotMarketIndexes: spotMarketIndexes,
564+
oracleInfos,
565+
userStats: true,
566+
accountSubscription: {
567+
type: 'polling',
568+
accountLoader: bulkAccountLoader,
569+
},
570+
});
571+
572+
await takerDriftClient.subscribe();
573+
await takerDriftClient.initializeUserAccountAndDepositCollateral(
574+
usdcAmount,
575+
userUSDCAccount.publicKey
576+
);
577+
578+
const takerDriftClientUser = new User({
579+
driftClient: takerDriftClient,
580+
userAccountPublicKey: await takerDriftClient.getUserAccountPublicKey(),
581+
accountSubscription: {
582+
type: 'polling',
583+
accountLoader: bulkAccountLoader,
584+
},
585+
});
586+
await takerDriftClientUser.subscribe();
587+
588+
const marketIndex = 0;
589+
const baseAssetAmount = BASE_PRECISION;
590+
const takerOrderParams = getLimitOrderParams({
591+
marketIndex,
592+
direction: PositionDirection.LONG,
593+
baseAssetAmount: baseAssetAmount.muln(2),
594+
price: new BN(34).mul(PRICE_PRECISION),
595+
userOrderId: 1,
596+
postOnly: PostOnlyParams.NONE,
597+
marketType: MarketType.PERP,
598+
});
599+
const uuid = Uint8Array.from(Buffer.from(nanoid(8)));
600+
const takerOrderParamsMessage: SignedMsgOrderParamsMessage = {
601+
signedMsgOrderParams: takerOrderParams,
602+
subAccountId: 0,
603+
slot: new BN((await connection.getSlot()) + 2),
604+
uuid,
605+
takeProfitOrderParams: null,
606+
stopLossOrderParams: null,
607+
};
608+
609+
await takerDriftClientUser.fetchAccounts();
610+
611+
const makerOrderParams = getLimitOrderParams({
612+
marketIndex,
613+
direction: PositionDirection.SHORT,
614+
baseAssetAmount: BASE_PRECISION,
615+
price: new BN(33).mul(PRICE_PRECISION),
616+
userOrderId: 1,
617+
postOnly: PostOnlyParams.MUST_POST_ONLY,
618+
bitFlags: 1,
619+
});
620+
621+
const signedOrderParams = takerDriftClient.signSignedMsgOrderParamsMessage(
622+
takerOrderParamsMessage
623+
);
624+
625+
const ixs = [
626+
ComputeBudgetProgram.setComputeUnitLimit({
627+
units: 10_000_000,
628+
}),
629+
];
630+
ixs.push(
631+
...(await makerDriftClient.getPlaceAndMakeSignedMsgPerpOrderIxs(
632+
signedOrderParams,
633+
uuid,
634+
{
635+
taker: await takerDriftClient.getUserAccountPublicKey(),
636+
takerUserAccount: takerDriftClient.getUserAccount(),
637+
takerStats: takerDriftClient.getUserStatsAccountPublicKey(),
638+
signingAuthority: takerDriftClient.authority,
639+
},
640+
makerOrderParams,
641+
undefined,
642+
undefined,
643+
ixs
644+
))
645+
);
646+
647+
const message = new TransactionMessage({
648+
instructions: ixs,
649+
payerKey: makerDriftClient.wallet.payer.publicKey,
650+
recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
651+
}).compileToV0Message();
652+
const tx = new VersionedTransaction(message);
653+
const simResult = await provider.connection.simulateTransaction(tx);
654+
console.log(simResult.value.logs);
655+
assert(simResult.value.err === null);
656+
657+
const normalTx = new Transaction();
658+
normalTx.add(...ixs);
659+
await makerDriftClient.sendTransaction(normalTx);
660+
661+
await takerDriftClientUser.fetchAccounts();
662+
663+
const perpPosition = await takerDriftClientUser.getPerpPosition(
664+
marketIndex
665+
);
666+
assert.equal(
667+
perpPosition.baseAssetAmount.toNumber(),
668+
baseAssetAmount.toNumber()
669+
);
670+
});
671+
672+
it('limit max slot breached', async () => {
673+
const keypair = new Keypair();
674+
await provider.connection.requestAirdrop(keypair.publicKey, 10 ** 9);
675+
await sleep(1000);
676+
const wallet = new Wallet(keypair);
677+
const userUSDCAccount = await mockUserUSDCAccount(
678+
usdcMint,
679+
usdcAmount,
680+
provider,
681+
keypair.publicKey
682+
);
683+
const takerDriftClient = new TestClient({
684+
connection,
685+
wallet,
686+
programID: chProgram.programId,
687+
opts: {
688+
commitment: 'confirmed',
689+
},
690+
activeSubAccountId: 0,
691+
perpMarketIndexes: marketIndexes,
692+
spotMarketIndexes: spotMarketIndexes,
693+
oracleInfos,
694+
userStats: true,
695+
accountSubscription: {
696+
type: 'polling',
697+
accountLoader: bulkAccountLoader,
698+
},
699+
});
700+
701+
await takerDriftClient.subscribe();
702+
await takerDriftClient.initializeUserAccountAndDepositCollateral(
703+
usdcAmount,
704+
userUSDCAccount.publicKey
705+
);
706+
707+
const takerDriftClientUser = new User({
708+
driftClient: takerDriftClient,
709+
userAccountPublicKey: await takerDriftClient.getUserAccountPublicKey(),
710+
accountSubscription: {
711+
type: 'polling',
712+
accountLoader: bulkAccountLoader,
713+
},
714+
});
715+
await takerDriftClientUser.subscribe();
716+
717+
const marketIndex = 0;
718+
const baseAssetAmount = BASE_PRECISION;
719+
const takerOrderParams = getLimitOrderParams({
720+
marketIndex,
721+
direction: PositionDirection.LONG,
722+
baseAssetAmount: baseAssetAmount.muln(2),
723+
price: new BN(34).mul(PRICE_PRECISION),
724+
userOrderId: 1,
725+
postOnly: PostOnlyParams.NONE,
726+
marketType: MarketType.PERP,
727+
});
728+
const uuid = Uint8Array.from(Buffer.from(nanoid(8)));
729+
const takerOrderParamsMessage: SignedMsgOrderParamsMessage = {
730+
signedMsgOrderParams: takerOrderParams,
731+
subAccountId: 0,
732+
slot: new BN((await connection.getSlot()) - 1),
733+
uuid,
734+
takeProfitOrderParams: null,
735+
stopLossOrderParams: null,
736+
};
737+
738+
await takerDriftClientUser.fetchAccounts();
739+
740+
const makerOrderParams = getLimitOrderParams({
741+
marketIndex,
742+
direction: PositionDirection.SHORT,
743+
baseAssetAmount: BASE_PRECISION,
744+
price: new BN(33).mul(PRICE_PRECISION),
745+
userOrderId: 1,
746+
postOnly: PostOnlyParams.MUST_POST_ONLY,
747+
bitFlags: 1,
748+
});
749+
750+
const signedOrderParams = takerDriftClient.signSignedMsgOrderParamsMessage(
751+
takerOrderParamsMessage
752+
);
753+
754+
const ixs = [
755+
ComputeBudgetProgram.setComputeUnitLimit({
756+
units: 10_000_000,
757+
}),
758+
];
759+
ixs.push(
760+
...(await makerDriftClient.getPlaceAndMakeSignedMsgPerpOrderIxs(
761+
signedOrderParams,
762+
uuid,
763+
{
764+
taker: await takerDriftClient.getUserAccountPublicKey(),
765+
takerUserAccount: takerDriftClient.getUserAccount(),
766+
takerStats: takerDriftClient.getUserStatsAccountPublicKey(),
767+
signingAuthority: takerDriftClient.authority,
768+
},
769+
makerOrderParams,
770+
undefined,
771+
undefined,
772+
ixs
773+
))
774+
);
775+
776+
const message = new TransactionMessage({
777+
instructions: ixs,
778+
payerKey: makerDriftClient.wallet.payer.publicKey,
779+
recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
780+
}).compileToV0Message();
781+
const tx = new VersionedTransaction(message);
782+
const simResult = await provider.connection.simulateTransaction(tx);
783+
console.log(simResult.value.logs);
784+
assert(
785+
simResult.value.logs.some((log) =>
786+
log.includes('SignedMsgOrderDoesNotExist')
787+
)
788+
);
789+
});
546790
});

0 commit comments

Comments
 (0)
Please sign in to comment.