The FHE orderbook implements four instructions for managing orders.
Instruction Enum
#[repr( u8 )]
#[derive( Clone , Copy , Debug , PartialEq )]
pub enum OrderbookInstruction {
Initialize = 0 ,
SubmitOrder = 1 ,
MatchOrders = 2 ,
CancelOrder = 3 ,
}
Initialize
Creates a new orderbook with the signer as authority.
Accounts
# Account Writable Signer Description 0 Orderbook Yes No Orderbook account to initialize 1 Authority No Yes Authority for the orderbook
Implementation
pub fn initialize_orderbook (
_program_id : & Pubkey ,
accounts : & [ AccountInfo ],
_instruction_data : & [ u8 ],
) -> ProgramResult {
let account_info_iter = & mut accounts . iter ();
let orderbook_account = next_account_info ( account_info_iter ) ? ;
let authority_account = next_account_info ( account_info_iter ) ? ;
if ! authority_account . is_signer {
return Err ( ProgramError :: MissingRequiredSignature );
}
let orderbook = Orderbook {
authority : * authority_account . key,
next_order_id : 0 ,
total_orders : 0 ,
total_matches : 0 ,
};
orderbook . serialize ( & mut * orderbook_account . data . borrow_mut ()) ? ;
msg! ( "Orderbook initialized" );
Ok (())
}
SubmitOrder
Submits a new order with encrypted price and quantity references.
Accounts
# Account Writable Signer Description 0 Orderbook Yes No Orderbook to update counters 1 Order Yes No Order account to create 2 Owner No Yes Order owner (signer)
Instruction Data
#[derive( BorshDeserialize )]
struct SubmitOrderData {
price_ref : EncryptedRef < u8 >, // 32 bytes
qty_ref : EncryptedRef < u8 >, // 32 bytes
side : OrderSide , // 1 byte
}
Implementation
pub fn submit_order (
_program_id : & Pubkey ,
accounts : & [ AccountInfo ],
instruction_data : & [ u8 ],
) -> ProgramResult {
let account_info_iter = & mut accounts . iter ();
let orderbook_account = next_account_info ( account_info_iter ) ? ;
let order_account = next_account_info ( account_info_iter ) ? ;
let owner_account = next_account_info ( account_info_iter ) ? ;
if ! owner_account . is_signer {
return Err ( ProgramError :: MissingRequiredSignature );
}
// Deserialize instruction data with typed EncryptedRef
let order_data = SubmitOrderData :: try_from_slice ( instruction_data ) ? ;
// Load and update orderbook
let mut orderbook = Orderbook :: try_from_slice ( & orderbook_account . data . borrow ()) ? ;
let order_id = orderbook . next_order_id;
orderbook . next_order_id += 1 ;
orderbook . total_orders += 1 ;
orderbook . serialize ( & mut * orderbook_account . data . borrow_mut ()) ? ;
// Create order (only 106 bytes!)
let order = Order {
owner : * owner_account . key,
price_ref : order_data . price_ref,
qty_ref : order_data . qty_ref,
side : order_data . side,
status : OrderStatus :: Open ,
order_id ,
};
order . serialize ( & mut * order_account . data . borrow_mut ()) ? ;
msg! ( "Order {} submitted" , order_id );
Ok (())
}
The encrypted data is not passed in the instruction - only the 32-byte hash references. The actual ciphertexts must be submitted separately via submitFheData RPC.
MatchOrders
Matches a buy and sell order using FHE operations.
Accounts
# Account Writable Signer Description 0 Orderbook Yes No Update match count 1 Buy Order Yes No Buy order to match 2 Sell Order Yes No Sell order to match 3 Match Result Yes No Store match result 4-10 Auth PDAs Optional No For decryption authorization
Implementation
pub fn match_orders (
_program_id : & Pubkey ,
accounts : & [ AccountInfo ],
_instruction_data : & [ u8 ],
) -> ProgramResult {
let account_info_iter = & mut accounts . iter ();
let orderbook_account = next_account_info ( account_info_iter ) ? ;
let buy_order_account = next_account_info ( account_info_iter ) ? ;
let sell_order_account = next_account_info ( account_info_iter ) ? ;
let match_result_account = next_account_info ( account_info_iter ) ? ;
// Load orders (small - only 106 bytes each)
let mut buy_order = Order :: try_from_slice ( & buy_order_account . data . borrow ()) ? ;
let mut sell_order = Order :: try_from_slice ( & sell_order_account . data . borrow ()) ? ;
// Verify order sides and status
if buy_order . side != OrderSide :: Buy || sell_order . side != OrderSide :: Sell {
return Err ( ProgramError :: InvalidArgument );
}
if buy_order . status != OrderStatus :: Open || sell_order . status != OrderStatus :: Open {
return Err ( ProgramError :: InvalidArgument );
}
// Load encrypted data using type-safe EncryptedRef.load()
let buy_price = buy_order . price_ref . load () ? ;
let sell_price = sell_order . price_ref . load () ? ;
let buy_qty = buy_order . qty_ref . load () ? ;
let sell_qty = sell_order . qty_ref . load () ? ;
// FHE comparison: buy_price >= sell_price
let _can_match = buy_price . ge ( & sell_price ) ? ;
// Calculate fill quantity: min(buy_qty, sell_qty)
let fill_qty = buy_qty . min ( & sell_qty ) ? ;
// Use sell price as fill price
let fill_price_ref = sell_order . price_ref;
// Store computed fill quantity
let fill_qty_ref = fill_qty . store () ? ;
// Update order statuses
buy_order . status = OrderStatus :: Matched ;
sell_order . status = OrderStatus :: Matched ;
buy_order . serialize ( & mut * buy_order_account . data . borrow_mut ()) ? ;
sell_order . serialize ( & mut * sell_order_account . data . borrow_mut ()) ? ;
// Update orderbook
let mut orderbook = Orderbook :: try_from_slice ( & orderbook_account . data . borrow ()) ? ;
let match_id = orderbook . total_matches;
orderbook . total_matches += 1 ;
orderbook . serialize ( & mut * orderbook_account . data . borrow_mut ()) ? ;
// Create match result
let match_result = MatchResult {
buy_order_id : buy_order . order_id,
sell_order_id : sell_order . order_id,
fill_price_ref ,
fill_qty_ref ,
match_id ,
};
match_result . serialize ( & mut * match_result_account . data . borrow_mut ()) ? ;
Ok (())
}
FHE Operations Summary
Operation SDK Method Description Load price price_ref.load()Fetch from store Compare buy_price.ge(&sell_price)Encrypted comparison Min buy_qty.min(&sell_qty)Encrypted minimum Store fill_qty.store()Save to store
CancelOrder
Cancels an open order (owner only).
Accounts
# Account Writable Signer Description 0 Order Yes No Order to cancel 1 Owner No Yes Order owner (signer)
Implementation
pub fn cancel_order (
_program_id : & Pubkey ,
accounts : & [ AccountInfo ],
_instruction_data : & [ u8 ],
) -> ProgramResult {
let account_info_iter = & mut accounts . iter ();
let order_account = next_account_info ( account_info_iter ) ? ;
let owner_account = next_account_info ( account_info_iter ) ? ;
if ! owner_account . is_signer {
return Err ( ProgramError :: MissingRequiredSignature );
}
let mut order = Order :: try_from_slice ( & order_account . data . borrow ()) ? ;
if order . owner != * owner_account . key {
msg! ( "Only order owner can cancel" );
return Err ( ProgramError :: InvalidArgument );
}
if order . status != OrderStatus :: Open {
msg! ( "Can only cancel open orders" );
return Err ( ProgramError :: InvalidArgument );
}
order . status = OrderStatus :: Cancelled ;
order . serialize ( & mut * order_account . data . borrow_mut ()) ? ;
msg! ( "Order {} cancelled" , order . order_id);
Ok (())
}
Entry Point
privora_sdk_program :: setup_fhe_allocator! ();
#[cfg(not(feature = "no-entrypoint" ))]
solana_program_entrypoint :: entrypoint_no_alloc! ( process_instruction );
pub fn process_instruction (
program_id : & Pubkey ,
accounts : & [ AccountInfo ],
instruction_data : & [ u8 ],
) -> ProgramResult {
let ( discriminant , data ) = instruction_data . split_at ( 1 );
let instruction = OrderbookInstruction :: from_u8 ( discriminant [ 0 ])
. ok_or ( ProgramError :: InvalidInstructionData ) ? ;
match instruction {
OrderbookInstruction :: Initialize => {
instructions :: initialize_orderbook ( program_id , accounts , data )
}
OrderbookInstruction :: SubmitOrder => {
instructions :: submit_order ( program_id , accounts , data )
}
OrderbookInstruction :: MatchOrders => {
instructions :: match_orders ( program_id , accounts , data )
}
OrderbookInstruction :: CancelOrder => {
instructions :: cancel_order ( program_id , accounts , data )
}
}
}
Next Steps
Matching Logic Deep dive into the FHE comparison and min operations