Skip to content

Commit 5341b48

Browse files
authored
Merge pull request #51 from Turbin3/feature/match-orders-27
[2.7] match_orders: crank-fill, partial/full match, fee calc, self-match guard
2 parents 165e963 + b52f405 commit 5341b48

2 files changed

Lines changed: 558 additions & 2 deletions

File tree

src/processor.rs

Lines changed: 191 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,10 +1077,199 @@ pub fn update_orderbook(
10771077
/// Account context: MatchOrdersAccounts (7 accounts)
10781078
/// Args: MatchOrdersArgs
10791079
pub fn match_orders(
1080-
_program_id: &Address,
1081-
_accounts: &mut [AccountView],
1080+
program_id: &Address,
1081+
accounts: &mut [AccountView],
10821082
_instruction_data: &[u8],
10831083
) -> ProgramResult {
1084+
let ctx = crate::instruction::MatchOrdersAccounts::try_from(&*accounts)?;
1085+
1086+
if !ctx.market.owned_by(program_id) {
1087+
return Err(FluxDexError::InvalidAccountOwner.into());
1088+
}
1089+
1090+
let (m_authority, m_base_mint, m_quote_mint, m_bump, m_fee_bps) = {
1091+
let data = ctx
1092+
.market
1093+
.try_borrow()
1094+
.map_err(|_| FluxDexError::InvalidInstructionData)?;
1095+
let market = Market::from_account(&data)?;
1096+
(
1097+
market.authority,
1098+
market.base_mint,
1099+
market.quote_mint,
1100+
market.bump,
1101+
market.fee_bps(),
1102+
)
1103+
};
1104+
1105+
if ctx.crank_authority.address().to_bytes() != m_authority {
1106+
return Err(FluxDexError::UnauthorizedCrank.into());
1107+
}
1108+
1109+
let market_pda = Address::create_program_address(
1110+
&[b"market", &m_base_mint, &m_quote_mint, &[m_bump]],
1111+
program_id,
1112+
)
1113+
.map_err(|_| FluxDexError::InvalidPdaSeeds)?;
1114+
if &market_pda != ctx.market.address() {
1115+
return Err(FluxDexError::InvalidPdaSeeds.into());
1116+
}
1117+
1118+
let market_bytes = ctx.market.address().to_bytes();
1119+
let (orderbook_pda, _) =
1120+
Address::find_program_address(&[b"orderbook", &market_bytes], program_id);
1121+
if &orderbook_pda != ctx.orderbook.address() {
1122+
return Err(FluxDexError::InvalidPdaSeeds.into());
1123+
}
1124+
1125+
if !ctx.buyer_order.owned_by(program_id) || !ctx.seller_order.owned_by(program_id) {
1126+
return Err(FluxDexError::InvalidAccountOwner.into());
1127+
}
1128+
1129+
let (b_side, b_status, b_price, b_quantity, b_filled, b_trader, b_id, b_bump, b_market) = {
1130+
let data = ctx
1131+
.buyer_order
1132+
.try_borrow()
1133+
.map_err(|_| FluxDexError::InvalidInstructionData)?;
1134+
let o = Order::from_account(&data)?;
1135+
(
1136+
o.side()?,
1137+
o.status()?,
1138+
o.price(),
1139+
o.quantity(),
1140+
o.filled(),
1141+
o.trader,
1142+
o.client_order_id(),
1143+
o.bump,
1144+
o.market,
1145+
)
1146+
};
1147+
1148+
let (s_side, s_status, s_price, s_quantity, s_filled, s_trader, s_id, s_bump, s_market) = {
1149+
let data = ctx
1150+
.seller_order
1151+
.try_borrow()
1152+
.map_err(|_| FluxDexError::InvalidInstructionData)?;
1153+
let o = Order::from_account(&data)?;
1154+
(
1155+
o.side()?,
1156+
o.status()?,
1157+
o.price(),
1158+
o.quantity(),
1159+
o.filled(),
1160+
o.trader,
1161+
o.client_order_id(),
1162+
o.bump,
1163+
o.market,
1164+
)
1165+
};
1166+
1167+
if b_market != market_bytes || s_market != market_bytes {
1168+
return Err(FluxDexError::InvalidArgument.into());
1169+
}
1170+
1171+
let b_order_pda = Address::create_program_address(
1172+
&[
1173+
b"order",
1174+
&market_bytes,
1175+
&b_trader,
1176+
&b_id.to_le_bytes(),
1177+
&[b_bump],
1178+
],
1179+
program_id,
1180+
)
1181+
.map_err(|_| FluxDexError::InvalidPdaSeeds)?;
1182+
if &b_order_pda != ctx.buyer_order.address() {
1183+
return Err(FluxDexError::InvalidPdaSeeds.into());
1184+
}
1185+
1186+
let s_order_pda = Address::create_program_address(
1187+
&[
1188+
b"order",
1189+
&market_bytes,
1190+
&s_trader,
1191+
&s_id.to_le_bytes(),
1192+
&[s_bump],
1193+
],
1194+
program_id,
1195+
)
1196+
.map_err(|_| FluxDexError::InvalidPdaSeeds)?;
1197+
if &s_order_pda != ctx.seller_order.address() {
1198+
return Err(FluxDexError::InvalidPdaSeeds.into());
1199+
}
1200+
1201+
if b_status != OrderStatus::Open || s_status != OrderStatus::Open {
1202+
return Err(FluxDexError::OrderNotOpen.into());
1203+
}
1204+
1205+
if b_side != Side::Buy || s_side != Side::Sell {
1206+
return Err(FluxDexError::InvalidArgument.into());
1207+
}
1208+
1209+
if b_price != s_price {
1210+
return Err(FluxDexError::InvalidArgument.into());
1211+
}
1212+
1213+
if b_trader == s_trader {
1214+
return Err(FluxDexError::InvalidArgument.into());
1215+
}
1216+
1217+
let b_remaining = b_quantity
1218+
.checked_sub(b_filled)
1219+
.ok_or(FluxDexError::ArithmeticOverflow)?;
1220+
let s_remaining = s_quantity
1221+
.checked_sub(s_filled)
1222+
.ok_or(FluxDexError::ArithmeticOverflow)?;
1223+
1224+
if b_remaining == 0 || s_remaining == 0 {
1225+
return Err(FluxDexError::InvalidArgument.into());
1226+
}
1227+
1228+
let fill_quantity = b_remaining.min(s_remaining);
1229+
let fill_price = b_price;
1230+
1231+
let _fee = fill_price
1232+
.checked_mul(fill_quantity)
1233+
.ok_or(FluxDexError::ArithmeticOverflow)?
1234+
.checked_mul(m_fee_bps as u64)
1235+
.ok_or(FluxDexError::ArithmeticOverflow)?
1236+
.checked_div(10000)
1237+
.ok_or(FluxDexError::ArithmeticOverflow)?;
1238+
1239+
let new_b_filled = b_filled
1240+
.checked_add(fill_quantity)
1241+
.ok_or(FluxDexError::ArithmeticOverflow)?;
1242+
let new_s_filled = s_filled
1243+
.checked_add(fill_quantity)
1244+
.ok_or(FluxDexError::ArithmeticOverflow)?;
1245+
1246+
let b_fully_filled = new_b_filled == b_quantity;
1247+
let s_fully_filled = new_s_filled == s_quantity;
1248+
1249+
{
1250+
let buyer_account = &mut accounts[5]; // MatchOrdersAccounts buyer_order index
1251+
let mut buyer_data = buyer_account
1252+
.try_borrow_mut()
1253+
.map_err(|_| FluxDexError::InvalidInstructionData)?;
1254+
let order = Order::from_account_mut(&mut buyer_data)?;
1255+
order.filled = new_b_filled.to_le_bytes();
1256+
if b_fully_filled {
1257+
order.status = u8::from(OrderStatus::Filled);
1258+
}
1259+
}
1260+
1261+
{
1262+
let seller_account = &mut accounts[6]; // MatchOrdersAccounts seller_order index
1263+
let mut seller_data = seller_account
1264+
.try_borrow_mut()
1265+
.map_err(|_| FluxDexError::InvalidInstructionData)?;
1266+
let order = Order::from_account_mut(&mut seller_data)?;
1267+
order.filled = new_s_filled.to_le_bytes();
1268+
if s_fully_filled {
1269+
order.status = u8::from(OrderStatus::Filled);
1270+
}
1271+
}
1272+
10841273
Ok(())
10851274
}
10861275

0 commit comments

Comments
 (0)