lightcone_sdk/program/
builder.rs1use solana_sdk::{pubkey::Pubkey, signature::Keypair};
4
5use crate::program::orders::FullOrder;
6use crate::program::types::OrderSide;
7use crate::shared::SubmitOrderRequest;
8
9#[derive(Debug, Clone, Default)]
32pub struct OrderBuilder {
33 nonce: Option<u64>,
34 maker: Option<Pubkey>,
35 market: Option<Pubkey>,
36 base_mint: Option<Pubkey>,
37 quote_mint: Option<Pubkey>,
38 side: Option<OrderSide>,
39 maker_amount: Option<u64>,
40 taker_amount: Option<u64>,
41 expiration: i64,
42}
43
44impl OrderBuilder {
45 pub fn new() -> Self {
47 Self::default()
48 }
49
50 pub fn nonce(mut self, nonce: u64) -> Self {
54 self.nonce = Some(nonce);
55 self
56 }
57
58 pub fn maker(mut self, maker: Pubkey) -> Self {
62 self.maker = Some(maker);
63 self
64 }
65
66 pub fn market(mut self, market: Pubkey) -> Self {
68 self.market = Some(market);
69 self
70 }
71
72 pub fn base_mint(mut self, base_mint: Pubkey) -> Self {
74 self.base_mint = Some(base_mint);
75 self
76 }
77
78 pub fn quote_mint(mut self, quote_mint: Pubkey) -> Self {
80 self.quote_mint = Some(quote_mint);
81 self
82 }
83
84 pub fn bid(mut self) -> Self {
86 self.side = Some(OrderSide::Bid);
87 self
88 }
89
90 pub fn ask(mut self) -> Self {
92 self.side = Some(OrderSide::Ask);
93 self
94 }
95
96 pub fn side(mut self, side: OrderSide) -> Self {
98 self.side = Some(side);
99 self
100 }
101
102 pub fn maker_amount(mut self, amount: u64) -> Self {
104 self.maker_amount = Some(amount);
105 self
106 }
107
108 pub fn taker_amount(mut self, amount: u64) -> Self {
110 self.taker_amount = Some(amount);
111 self
112 }
113
114 pub fn expiration(mut self, expiration: i64) -> Self {
116 self.expiration = expiration;
117 self
118 }
119
120 pub fn build(self) -> FullOrder {
129 FullOrder {
130 nonce: self.nonce.expect("nonce is required"),
131 maker: self.maker.expect("maker is required"),
132 market: self.market.expect("market is required"),
133 base_mint: self.base_mint.expect("base_mint is required"),
134 quote_mint: self.quote_mint.expect("quote_mint is required"),
135 side: self.side.expect("side is required (call .bid() or .ask())"),
136 maker_amount: self.maker_amount.expect("maker_amount is required"),
137 taker_amount: self.taker_amount.expect("taker_amount is required"),
138 expiration: self.expiration,
139 signature: [0u8; 64],
140 }
141 }
142
143 pub fn build_and_sign(self, keypair: &Keypair) -> FullOrder {
151 let mut order = self.build();
152 order.sign(keypair);
153 order
154 }
155
156 pub fn to_submit_request(
167 self,
168 keypair: &Keypair,
169 orderbook_id: impl Into<String>,
170 ) -> SubmitOrderRequest {
171 self.build_and_sign(keypair).to_submit_request(orderbook_id)
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use solana_sdk::signer::Signer;
179
180 #[test]
181 fn test_order_builder_basic() {
182 let keypair = Keypair::new();
183 let maker = keypair.pubkey();
184 let market = Pubkey::new_unique();
185 let base_mint = Pubkey::new_unique();
186 let quote_mint = Pubkey::new_unique();
187
188 let order = OrderBuilder::new()
189 .nonce(1)
190 .maker(maker)
191 .market(market)
192 .base_mint(base_mint)
193 .quote_mint(quote_mint)
194 .bid()
195 .maker_amount(1_000_000)
196 .taker_amount(500_000)
197 .build_and_sign(&keypair);
198
199 assert_eq!(order.nonce, 1);
200 assert_eq!(order.maker, maker);
201 assert_eq!(order.market, market);
202 assert_eq!(order.base_mint, base_mint);
203 assert_eq!(order.quote_mint, quote_mint);
204 assert_eq!(order.side, OrderSide::Bid);
205 assert_eq!(order.maker_amount, 1_000_000);
206 assert_eq!(order.taker_amount, 500_000);
207 assert!(order.is_signed());
208 }
209
210 #[test]
211 fn test_order_builder_to_submit_request() {
212 let keypair = Keypair::new();
213 let maker = keypair.pubkey();
214 let market = Pubkey::new_unique();
215 let base_mint = Pubkey::new_unique();
216 let quote_mint = Pubkey::new_unique();
217
218 let request = OrderBuilder::new()
219 .nonce(1)
220 .maker(maker)
221 .market(market)
222 .base_mint(base_mint)
223 .quote_mint(quote_mint)
224 .ask()
225 .maker_amount(500_000)
226 .taker_amount(1_000_000)
227 .to_submit_request(&keypair, "test_orderbook");
228
229 assert_eq!(request.maker, maker.to_string());
230 assert_eq!(request.nonce, 1);
231 assert_eq!(request.market_pubkey, market.to_string());
232 assert_eq!(request.base_token, base_mint.to_string());
233 assert_eq!(request.quote_token, quote_mint.to_string());
234 assert_eq!(request.side, 1); assert_eq!(request.maker_amount, 500_000);
236 assert_eq!(request.taker_amount, 1_000_000);
237 assert_eq!(request.orderbook_id, "test_orderbook");
238 assert_eq!(request.signature.len(), 128); }
240
241 #[test]
242 fn test_order_builder_unsigned() {
243 let keypair = Keypair::new();
244 let order = OrderBuilder::new()
245 .nonce(1)
246 .maker(keypair.pubkey())
247 .market(Pubkey::new_unique())
248 .base_mint(Pubkey::new_unique())
249 .quote_mint(Pubkey::new_unique())
250 .bid()
251 .maker_amount(1_000_000)
252 .taker_amount(500_000)
253 .build();
254
255 assert!(!order.is_signed());
256 }
257
258 #[test]
259 #[should_panic(expected = "nonce is required")]
260 fn test_order_builder_missing_nonce() {
261 let keypair = Keypair::new();
262 OrderBuilder::new()
263 .maker(keypair.pubkey())
264 .market(Pubkey::new_unique())
265 .base_mint(Pubkey::new_unique())
266 .quote_mint(Pubkey::new_unique())
267 .bid()
268 .maker_amount(1_000_000)
269 .taker_amount(500_000)
270 .build_and_sign(&keypair);
271 }
272
273 #[test]
274 #[should_panic(expected = "side is required")]
275 fn test_order_builder_missing_side() {
276 let keypair = Keypair::new();
277 OrderBuilder::new()
278 .nonce(1)
279 .maker(keypair.pubkey())
280 .market(Pubkey::new_unique())
281 .base_mint(Pubkey::new_unique())
282 .quote_mint(Pubkey::new_unique())
283 .maker_amount(1_000_000)
284 .taker_amount(500_000)
285 .build_and_sign(&keypair);
286 }
287}