Skip to content

Commit a18f763

Browse files
swift upgrade for delegates (#1546)
* swift: set taker pubkey for delegates * upgrade swift with delegates * fix tests * changelog
1 parent 5053285 commit a18f763

File tree

8 files changed

+311
-69
lines changed

8 files changed

+311
-69
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020

2121
- program: fix reference price offset reserves ([#1516](https://github.com/drift-labs/protocol-v2/pull/1516))
2222
- sdk: account for authority when useMarketLastSlotCache ([#1541](https://github.com/drift-labs/protocol-v2/pull/1541))
23+
- program: delegate wallets sign taker pubkey into message ([#1546](https://github.com/drift-labs/protocol-v2/pull/1546))
2324

2425
### Breaking
2526

programs/drift/src/instructions/keeper.rs

+37-29
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use crate::state::user::{
6262
MarginMode, MarketType, OrderStatus, OrderTriggerCondition, OrderType, User, UserStats,
6363
};
6464
use crate::state::user_map::{load_user_map, load_user_maps, UserMap, UserStatsMap};
65-
use crate::validation::sig_verification::verify_ed25519_msg;
65+
use crate::validation::sig_verification::verify_and_decode_ed25519_msg;
6666
use crate::validation::user::{validate_user_deletion, validate_user_is_idle};
6767
use crate::{
6868
controller, load, math, print_error, safe_decrement, OracleSource, GOV_SPOT_MARKET_INDEX,
@@ -665,44 +665,51 @@ pub fn place_signed_msg_taker_order<'c: 'info, 'info>(
665665
// Verify data from verify ix
666666
let ix: Instruction = load_instruction_at_checked(ix_idx as usize - 1, ix_sysvar)?;
667667

668-
let signer = match is_delegate_signer {
669-
true => taker.delegate.to_bytes(),
670-
false => taker.authority.to_bytes(),
668+
let signer = if is_delegate_signer {
669+
taker.delegate.to_bytes()
670+
} else {
671+
taker.authority.to_bytes()
671672
};
672-
let verified_message_and_signature = verify_ed25519_msg(
673+
let verified_message_and_signature = verify_and_decode_ed25519_msg(
673674
&ix,
674675
ix_sysvar,
675676
ix_idx,
676677
&signer,
677678
&taker_order_params_message_bytes[..],
679+
is_delegate_signer,
678680
)?;
679681

680-
let taker_order_params_message: SignedMsgOrderParamsMessage =
681-
verified_message_and_signature.signed_msg_order_params_message;
682-
683-
// Verify taker passed to the ix matches pda derived from subaccount id + authority
684-
let taker_pda = Pubkey::find_program_address(
685-
&[
686-
"user".as_bytes(),
687-
&taker.authority.to_bytes(),
688-
&taker_order_params_message.sub_account_id.to_le_bytes(),
689-
],
690-
&ID,
691-
);
692-
if taker_pda.0 != taker_key {
693-
msg!(
694-
"Taker key {:?} does not match pda {:?}",
695-
taker_key,
696-
taker_pda.0
682+
if is_delegate_signer {
683+
validate!(
684+
verified_message_and_signature.delegate_signed_taker_pubkey == Some(taker_key),
685+
ErrorCode::SignedMsgUserContextUserMismatch,
686+
"Delegate signed msg for taker pubkey different than supplied pubkey"
687+
)?;
688+
} else {
689+
// Verify taker passed to the ix matches pda derived from subaccount id + authority
690+
let taker_pda = Pubkey::find_program_address(
691+
&[
692+
"user".as_bytes(),
693+
&taker.authority.to_bytes(),
694+
&verified_message_and_signature
695+
.sub_account_id
696+
.unwrap()
697+
.to_le_bytes(),
698+
],
699+
&ID,
697700
);
698-
return Err(ErrorCode::SignedMsgUserContextUserMismatch.into());
699-
}
701+
validate!(
702+
taker_pda.0 == taker_key,
703+
ErrorCode::SignedMsgUserContextUserMismatch,
704+
"Taker key does not match pda"
705+
)?;
706+
};
700707

701708
let signature = verified_message_and_signature.signature;
702709
let clock = &Clock::get()?;
703710

704711
// First order must be a taker order
705-
let matching_taker_order_params = &taker_order_params_message.signed_msg_order_params;
712+
let matching_taker_order_params = &verified_message_and_signature.signed_msg_order_params;
706713
if matching_taker_order_params.market_type != MarketType::Perp
707714
|| !matching_taker_order_params.has_valid_auction_params()?
708715
{
@@ -720,7 +727,7 @@ pub fn place_signed_msg_taker_order<'c: 'info, 'info>(
720727
}
721728

722729
// Set max slot for the order early so we set correct signed msg order id
723-
let order_slot = taker_order_params_message.slot;
730+
let order_slot = verified_message_and_signature.slot;
724731
if order_slot < clock.slot.saturating_sub(500) {
725732
msg!(
726733
"SignedMsg order slot {} is too old: must be within 500 slots of current slot",
@@ -749,7 +756,7 @@ pub fn place_signed_msg_taker_order<'c: 'info, 'info>(
749756
// Dont place order if signed msg order already exists
750757
let mut taker_order_id_to_use = taker.next_order_id;
751758
let mut signed_msg_order_id =
752-
SignedMsgOrderId::new(taker_order_params_message.uuid, max_slot, 0);
759+
SignedMsgOrderId::new(verified_message_and_signature.uuid, max_slot, 0);
753760
if signed_msg_account
754761
.check_exists_and_prune_stale_signed_msg_order_ids(signed_msg_order_id, clock.slot)
755762
{
@@ -758,7 +765,7 @@ pub fn place_signed_msg_taker_order<'c: 'info, 'info>(
758765
}
759766

760767
// Good to place orders, do stop loss and take profit orders first
761-
if let Some(stop_loss_order_params) = taker_order_params_message.stop_loss_order_params {
768+
if let Some(stop_loss_order_params) = verified_message_and_signature.stop_loss_order_params {
762769
taker_order_id_to_use += 1;
763770
let stop_loss_order = OrderParams {
764771
order_type: OrderType::TriggerMarket,
@@ -793,7 +800,8 @@ pub fn place_signed_msg_taker_order<'c: 'info, 'info>(
793800
)?;
794801
}
795802

796-
if let Some(take_profit_order_params) = taker_order_params_message.take_profit_order_params {
803+
if let Some(take_profit_order_params) = verified_message_and_signature.take_profit_order_params
804+
{
797805
taker_order_id_to_use += 1;
798806
let take_profit_order = OrderParams {
799807
order_type: OrderType::TriggerMarket,

programs/drift/src/state/order_params.rs

+10
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,16 @@ pub struct SignedMsgOrderParamsMessage {
733733
pub stop_loss_order_params: Option<SignedMsgTriggerOrderParams>,
734734
}
735735

736+
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Eq, PartialEq, Debug)]
737+
pub struct SignedMsgOrderParamsDelegateMessage {
738+
pub signed_msg_order_params: OrderParams,
739+
pub taker_pubkey: Pubkey,
740+
pub slot: u64,
741+
pub uuid: [u8; 8],
742+
pub take_profit_order_params: Option<SignedMsgTriggerOrderParams>,
743+
pub stop_loss_order_params: Option<SignedMsgTriggerOrderParams>,
744+
}
745+
736746
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Eq, PartialEq, Debug)]
737747
pub struct SignedMsgTriggerOrderParams {
738748
pub trigger_price: u64,

programs/drift/src/validation/sig_verification.rs

+52-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use crate::error::ErrorCode;
2-
use crate::state::order_params::SignedMsgOrderParamsMessage;
2+
use crate::state::order_params::{
3+
OrderParams, SignedMsgOrderParamsDelegateMessage, SignedMsgOrderParamsMessage,
4+
SignedMsgTriggerOrderParams,
5+
};
36
use anchor_lang::prelude::*;
47
use bytemuck::try_cast_slice;
58
use bytemuck::{Pod, Zeroable};
@@ -10,6 +13,7 @@ use solana_program::instruction::Instruction;
1013
use solana_program::program_memory::sol_memcmp;
1114
use solana_program::sysvar;
1215
use std::convert::TryInto;
16+
use std::f32::consts::E;
1317

1418
const ED25519_PROGRAM_INPUT_HEADER_LEN: usize = 2;
1519

@@ -43,7 +47,13 @@ pub struct Ed25519SignatureOffsets {
4347
}
4448

4549
pub struct VerifiedMessage {
46-
pub signed_msg_order_params_message: SignedMsgOrderParamsMessage,
50+
pub signed_msg_order_params: OrderParams,
51+
pub sub_account_id: Option<u16>,
52+
pub delegate_signed_taker_pubkey: Option<Pubkey>,
53+
pub slot: u64,
54+
pub uuid: [u8; 8],
55+
pub take_profit_order_params: Option<SignedMsgTriggerOrderParams>,
56+
pub stop_loss_order_params: Option<SignedMsgTriggerOrderParams>,
4757
pub signature: [u8; 64],
4858
}
4959

@@ -59,12 +69,13 @@ fn slice_eq(a: &[u8], b: &[u8]) -> bool {
5969
///
6070
/// `pubkey` expected pubkey of the signer
6171
///
62-
pub fn verify_ed25519_msg(
72+
pub fn verify_and_decode_ed25519_msg(
6373
ed25519_ix: &Instruction,
6474
instructions_sysvar: &AccountInfo,
6575
current_ix_index: u16,
6676
signer: &[u8; 32],
6777
msg: &[u8],
78+
is_delegate_signer: bool,
6879
) -> Result<VerifiedMessage> {
6980
if ed25519_ix.program_id != ED25519_ID || ed25519_ix.accounts.len() != 0 {
7081
msg!("Invalid Ix: program ID: {:?}", ed25519_ix.program_id);
@@ -221,13 +232,46 @@ pub fn verify_ed25519_msg(
221232

222233
let payload =
223234
hex::decode(payload).map_err(|_| SignatureVerificationError::InvalidMessageHex)?;
224-
Ok(VerifiedMessage {
225-
signed_msg_order_params_message: SignedMsgOrderParamsMessage::deserialize(
235+
236+
if is_delegate_signer {
237+
let deserialized = SignedMsgOrderParamsDelegateMessage::deserialize(
238+
&mut &payload[8..], // 8 byte manual discriminator
239+
)
240+
.map_err(|_| {
241+
msg!("Invalid message encoding for is_delegate_signer = true");
242+
SignatureVerificationError::InvalidMessageDataSize
243+
})?;
244+
245+
return Ok(VerifiedMessage {
246+
signed_msg_order_params: deserialized.signed_msg_order_params,
247+
sub_account_id: None,
248+
delegate_signed_taker_pubkey: Some(deserialized.taker_pubkey),
249+
slot: deserialized.slot,
250+
uuid: deserialized.uuid,
251+
take_profit_order_params: deserialized.take_profit_order_params,
252+
stop_loss_order_params: deserialized.stop_loss_order_params,
253+
signature: *signature,
254+
});
255+
} else {
256+
let deserialized = SignedMsgOrderParamsMessage::deserialize(
226257
&mut &payload[8..], // 8 byte manual discriminator
227258
)
228-
.unwrap(),
229-
signature: *signature,
230-
})
259+
.map_err(|_| {
260+
msg!("Invalid delegate message encoding for with is_delegate_signer = false");
261+
SignatureVerificationError::InvalidMessageDataSize
262+
})?;
263+
264+
return Ok(VerifiedMessage {
265+
signed_msg_order_params: deserialized.signed_msg_order_params,
266+
sub_account_id: Some(deserialized.sub_account_id),
267+
delegate_signed_taker_pubkey: None,
268+
slot: deserialized.slot,
269+
uuid: deserialized.uuid,
270+
take_profit_order_params: deserialized.take_profit_order_params,
271+
stop_loss_order_params: deserialized.stop_loss_order_params,
272+
signature: *signature,
273+
});
274+
}
231275
}
232276

233277
#[error_code]

sdk/src/driftClient.ts

+22-7
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import {
5757
UserAccount,
5858
UserStatsAccount,
5959
ProtectedMakerModeConfig,
60+
SignedMsgOrderParamsDelegateMessage,
6061
} from './types';
6162
import driftIDL from './idl/drift.json';
6263

@@ -6323,9 +6324,15 @@ export class DriftClient {
63236324
}
63246325

63256326
public signSignedMsgOrderParamsMessage(
6326-
orderParamsMessage: SignedMsgOrderParamsMessage
6327+
orderParamsMessage:
6328+
| SignedMsgOrderParamsMessage
6329+
| SignedMsgOrderParamsDelegateMessage,
6330+
delegateSigner?: boolean
63276331
): SignedMsgOrderParams {
6328-
const borshBuf = this.encodeSignedMsgOrderParamsMessage(orderParamsMessage);
6332+
const borshBuf = this.encodeSignedMsgOrderParamsMessage(
6333+
orderParamsMessage,
6334+
delegateSigner
6335+
);
63296336
const orderParams = Buffer.from(borshBuf.toString('hex'));
63306337
return {
63316338
orderParams,
@@ -6337,16 +6344,24 @@ export class DriftClient {
63376344
* Borsh encode signedMsg taker order params
63386345
*/
63396346
public encodeSignedMsgOrderParamsMessage(
6340-
orderParamsMessage: SignedMsgOrderParamsMessage
6347+
orderParamsMessage:
6348+
| SignedMsgOrderParamsMessage
6349+
| SignedMsgOrderParamsDelegateMessage,
6350+
delegateSigner?: boolean
63416351
): Buffer {
63426352
const anchorIxName = 'global' + ':' + 'SignedMsgOrderParamsMessage';
63436353
const prefix = Buffer.from(sha256(anchorIxName).slice(0, 8));
63446354
const buf = Buffer.concat([
63456355
prefix,
6346-
this.program.coder.types.encode(
6347-
'SignedMsgOrderParamsMessage',
6348-
orderParamsMessage
6349-
),
6356+
delegateSigner
6357+
? this.program.coder.types.encode(
6358+
'SignedMsgOrderParamsDelegateMessage',
6359+
orderParamsMessage as SignedMsgOrderParamsDelegateMessage
6360+
)
6361+
: this.program.coder.types.encode(
6362+
'SignedMsgOrderParamsMessage',
6363+
orderParamsMessage as SignedMsgOrderParamsMessage
6364+
),
63506365
]);
63516366
return buf;
63526367
}

sdk/src/idl/drift.json

+47
Original file line numberDiff line numberDiff line change
@@ -9531,6 +9531,53 @@
95319531
]
95329532
}
95339533
},
9534+
{
9535+
"name": "SignedMsgOrderParamsDelegateMessage",
9536+
"type": {
9537+
"kind": "struct",
9538+
"fields": [
9539+
{
9540+
"name": "signedMsgOrderParams",
9541+
"type": {
9542+
"defined": "OrderParams"
9543+
}
9544+
},
9545+
{
9546+
"name": "takerPubkey",
9547+
"type": "publicKey"
9548+
},
9549+
{
9550+
"name": "slot",
9551+
"type": "u64"
9552+
},
9553+
{
9554+
"name": "uuid",
9555+
"type": {
9556+
"array": [
9557+
"u8",
9558+
8
9559+
]
9560+
}
9561+
},
9562+
{
9563+
"name": "takeProfitOrderParams",
9564+
"type": {
9565+
"option": {
9566+
"defined": "SignedMsgTriggerOrderParams"
9567+
}
9568+
}
9569+
},
9570+
{
9571+
"name": "stopLossOrderParams",
9572+
"type": {
9573+
"option": {
9574+
"defined": "SignedMsgTriggerOrderParams"
9575+
}
9576+
}
9577+
}
9578+
]
9579+
}
9580+
},
95349581
{
95359582
"name": "SignedMsgTriggerOrderParams",
95369583
"type": {

sdk/src/types.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,15 @@ export type SignedMsgOrderParamsMessage = {
11741174
stopLossOrderParams: SignedMsgTriggerOrderParams | null;
11751175
};
11761176

1177+
export type SignedMsgOrderParamsDelegateMessage = {
1178+
signedMsgOrderParams: OrderParams;
1179+
slot: BN;
1180+
uuid: Uint8Array;
1181+
takerPubkey: PublicKey;
1182+
takeProfitOrderParams: SignedMsgTriggerOrderParams | null;
1183+
stopLossOrderParams: SignedMsgTriggerOrderParams | null;
1184+
};
1185+
11771186
export type SignedMsgTriggerOrderParams = {
11781187
triggerPrice: BN;
11791188
baseAssetAmount: BN;

0 commit comments

Comments
 (0)