1use crate::{
2 CallOption,
3 call_handler_ext::CallHandlerExt,
4 market_data::{
5 OrderData,
6 order_book::Balances,
7 },
8 order_book::{
9 CreateOrderParams,
10 OrderBookManager,
11 OrderType,
12 },
13 order_book_deploy::{
14 OrderBookDeploy,
15 OrderCancelledEvent,
16 OrderCreatedEvent,
17 OrderMatchedEvent,
18 },
19 trade_account::{
20 CallContractArgs,
21 TradeAccountManager,
22 },
23 trade_account_deploy::{
24 DeployConfig,
25 TradeAccountDeploy,
26 },
27};
28use fuels::{
29 prelude::*,
30 programs::responses::CallResponse,
31 types::{
32 Address,
33 Bytes32,
34 ContractId,
35 Identity,
36 tx_status::TxStatus,
37 },
38};
39use std::collections::{
40 HashMap,
41 HashSet,
42};
43
44pub async fn setup_order_book<W: Account + Clone>(
45 deployer_wallet: &W,
46 base_asset: AssetId,
47 quote_asset: AssetId,
48 deploy_config: &crate::order_book_deploy::OrderBookDeployConfig,
49) -> anyhow::Result<OrderBookManager<W>, anyhow::Error> {
50 let order_book_deploy =
51 OrderBookDeploy::deploy(deployer_wallet, base_asset, quote_asset, deploy_config)
52 .await?;
53 let order_book = OrderBookManager::new(deployer_wallet, 9, 9, &order_book_deploy);
54 Ok(order_book)
55}
56
57pub async fn setup_trade_accounts(
58 deployer_wallet: &Wallet,
59 contract_ids: &[ContractId],
60 wallets: &mut Vec<Wallet>,
61) -> anyhow::Result<Vec<TradeAccountManager<Wallet>>, anyhow::Error> {
62 let config = crate::trade_account_deploy::TradeAccountDeployConfig::default();
63 let deploy_config = DeployConfig::Latest(config);
64 let trade_account_deploy: TradeAccountDeploy<Wallet> =
65 TradeAccountDeploy::deploy(deployer_wallet, &deploy_config).await?;
66 let mut trade_accounts = Vec::with_capacity(wallets.len());
67
68 for user_wallet in wallets {
70 let deployment = trade_account_deploy
71 .deploy_with_account(
72 &user_wallet.address().into(),
73 &deploy_config,
74 &CallOption::AwaitBlock,
75 None,
76 &[],
77 )
78 .await?;
79 let trade_account = TradeAccountManager::create_with_session(
80 &user_wallet.clone(),
81 &user_wallet.clone(),
82 contract_ids,
83 &deployment,
84 CallOption::AwaitBlock,
85 )
86 .await?;
87 trade_accounts.push(trade_account);
88 }
89
90 Ok(trade_accounts)
91}
92
93pub async fn fund_trade_accounts<W: Account + Clone>(
94 trade_accounts: &[TradeAccountManager<W>],
95 base_asset: AssetId,
96 base_asset_amount: u64,
97 quote_asset: AssetId,
98 quote_asset_amount: u64,
99) -> anyhow::Result<(), anyhow::Error> {
100 for trade_account in trade_accounts.iter() {
101 let _ = trade_account
102 .owner
103 .force_transfer_to_contract(
104 trade_account.contract.contract_id(),
105 base_asset_amount,
106 base_asset,
107 TxPolicies::default(),
108 )
109 .await?;
110 let _ = trade_account
111 .owner
112 .force_transfer_to_contract(
113 trade_account.contract.contract_id(),
114 quote_asset_amount,
115 quote_asset,
116 TxPolicies::default(),
117 )
118 .await?;
119 }
120 Ok(())
121}
122
123pub async fn create_order_call(
124 order_book: &OrderBookManager<Wallet>,
125 order_data: &OrderData,
126 order_type: OrderType,
127 trade_account: &mut TradeAccountManager<Wallet>,
128 gas_per_method: Option<u64>,
129) -> anyhow::Result<CallContractArgs> {
130 let create_order_params = CreateOrderParams {
131 price: order_data.price,
132 quantity: order_data.quantity,
133 side: order_data.side,
134 asset_id: order_book.get_order_side_asset(&order_data.side),
135 order_type,
136 };
137 let contract_call_args = trade_account
139 .create_order(order_book, &create_order_params, gas_per_method)
140 .await?;
141
142 Ok(contract_call_args)
143}
144
145pub async fn create_order_handlers<'a, I>(
146 order_book: &OrderBookManager<Wallet>,
147 orders: &[OrderData],
148 trade_accounts: I,
149 gas_per_method: Option<u64>,
150) -> anyhow::Result<
151 Vec<CallHandler<Wallet, fuels::programs::calls::ContractCall, ()>>,
152 anyhow::Error,
153>
154where
155 I: Iterator<Item = &'a mut TradeAccountManager<Wallet>>,
156{
157 let mut create_orders_handlers = Vec::with_capacity(orders.len());
158 let mut trade_accounts = trade_accounts.into_iter().collect::<Vec<_>>();
159 for order_data in orders.iter() {
160 let trade_account = trade_accounts
161 .iter_mut()
162 .find(|ta| ta.identity() == order_data.trader_id)
163 .unwrap();
164
165 let contract_call_args = create_order_call(
167 order_book,
168 order_data,
169 OrderType::Spot,
170 trade_account,
171 gas_per_method,
172 )
173 .await?;
174
175 create_orders_handlers.push(
177 trade_account
178 .session_call_contract(&contract_call_args)
179 .with_contract_ids(&[order_book.contract.contract_id()]),
180 );
181 }
182 Ok(create_orders_handlers)
183}
184
185pub async fn cancel_order_call(
186 order_book: &OrderBookManager<Wallet>,
187 order_id: &Bytes32,
188 trade_account: &mut TradeAccountManager<Wallet>,
189 gas_per_method: Option<u64>,
190) -> anyhow::Result<CallContractArgs> {
191 trade_account
192 .cancel_order(order_book, *order_id, gas_per_method)
193 .await
194}
195
196pub async fn cancel_order_handlers(
197 order_book: &OrderBookManager<Wallet>,
198 orders: &[Bytes32],
199 trade_account: &mut TradeAccountManager<Wallet>,
200 gas_per_method: Option<u64>,
201) -> anyhow::Result<
202 Vec<CallHandler<Wallet, fuels::programs::calls::ContractCall, ()>>,
203 anyhow::Error,
204> {
205 let mut cancel_orders_handlers = Vec::with_capacity(orders.len());
206 for order_id in orders.iter() {
207 let contract_call_args =
208 cancel_order_call(order_book, order_id, trade_account, gas_per_method)
209 .await?;
210
211 cancel_orders_handlers
213 .push(trade_account.session_call_contract(&contract_call_args));
214 }
215 Ok(cancel_orders_handlers)
216}
217
218pub async fn send_transactions(
219 create_orders_handlers: Vec<
220 CallHandler<Wallet, fuels::programs::calls::ContractCall, ()>,
221 >,
222 gaspayer_wallet: Wallet,
223 call_option: CallOption,
224) -> Vec<Bytes32> {
225 let mut order_results = Vec::with_capacity(create_orders_handlers.len());
226 for mut call_handler in create_orders_handlers {
227 call_handler.account = gaspayer_wallet.clone();
228
229 let tx_id = match call_option.clone() {
230 CallOption::AwaitBlock => call_handler.submit().await.unwrap().tx_id(),
231 CallOption::AwaitPreconfirmation(ops) => {
232 call_handler
233 .almost_sync_call(
234 &ops.data_builder,
235 &ops.utxo_manager,
236 &ops.tx_config,
237 None,
238 &[],
239 )
240 .await
241 .unwrap()
242 .tx_id
243 }
244 };
245 order_results.push(tx_id);
246 }
247 order_results
248}
249
250#[derive(Debug, Clone, Default)]
251pub struct OrderBookEvents {
252 pub matches: Vec<OrderMatchedEvent>,
253 pub orders: Vec<OrderCreatedEvent>,
254 pub cancels: Vec<OrderCancelledEvent>,
255}
256
257pub fn get_order_book_events<W: Account + Clone>(
258 order_book: &OrderBookManager<W>,
259 order_book_events: &mut OrderBookEvents,
260 tx_result: &TxStatus,
261) -> anyhow::Result<(), anyhow::Error> {
262 if let TxStatus::Success(success) = tx_result {
263 let mut order_created_events =
264 order_book
265 .contract
266 .log_decoder()
267 .decode_logs_with_type::<OrderCreatedEvent>(&success.receipts)?;
268 let mut order_match_events =
269 order_book
270 .contract
271 .log_decoder()
272 .decode_logs_with_type::<OrderMatchedEvent>(&success.receipts)?;
273 let mut order_cancel_events =
274 order_book
275 .contract
276 .log_decoder()
277 .decode_logs_with_type::<OrderCancelledEvent>(&success.receipts)?;
278
279 order_book_events.orders.append(&mut order_created_events);
280 order_book_events.matches.append(&mut order_match_events);
281 order_book_events.cancels.append(&mut order_cancel_events);
282 }
283 Ok(())
284}
285
286pub async fn get_wallets_balances<W: Account + Clone>(
287 wallets: &[W],
288 base_asset: AssetId,
289 quote_asset: AssetId,
290) -> anyhow::Result<HashMap<Address, (u128, u128)>, anyhow::Error> {
291 let mut balances = HashMap::new();
292 for wallet in wallets {
293 let balance = wallet.get_balances().await?;
294 let base_asset_balance =
295 balance.get(&base_asset.to_string()).cloned().unwrap_or(0);
296 let quote_asset_balance =
297 balance.get("e_asset.to_string()).cloned().unwrap_or(0);
298 balances.insert(wallet.address(), (base_asset_balance, quote_asset_balance));
299 }
300 Ok(balances)
301}
302
303pub async fn get_contracts_balances(
304 provider: &Provider,
305 contracts: &[ContractId],
306 base_asset: &AssetId,
307 quote_asset: &AssetId,
308) -> anyhow::Result<Balances, anyhow::Error> {
309 let mut balances = Balances::new();
310 for contract_id in contracts {
311 let balance = provider.get_contract_balances(contract_id).await?;
312 let base_asset_balance = balance.get(&base_asset.clone()).cloned().unwrap_or(0);
313 let quote_asset_balance = balance.get("e_asset.clone()).cloned().unwrap_or(0);
314 balances.insert(
315 Identity::ContractId(*contract_id),
316 (base_asset_balance, quote_asset_balance),
317 );
318 }
319 Ok(balances)
320}
321
322pub async fn settle_trade_accounts_balances<'a, I>(
323 fee_payer: &Wallet,
324 order_book: &OrderBookManager<Wallet>,
325 trade_accounts: I,
326 call_option: CallOption,
327) -> anyhow::Result<CallResponse<()>, anyhow::Error>
328where
329 I: Iterator<Item = &'a TradeAccountManager<Wallet>>,
330{
331 let mut accounts = vec![];
332 let mut contracts = vec![];
333 for account in trade_accounts {
334 accounts.push(Identity::from(account.contract.contract_id()));
335 contracts.push(account.contract.contract_id());
336 }
337
338 let mut call_handler = order_book
339 .contract
340 .methods()
341 .settle_balances(accounts)
342 .with_contract_ids(&contracts);
343 call_handler.account = fee_payer.clone();
344
345 let result = match call_option {
346 CallOption::AwaitBlock => call_handler.call().await?,
347 CallOption::AwaitPreconfirmation(ops) => {
348 call_handler
349 .almost_sync_call(
350 &ops.data_builder,
351 &ops.utxo_manager,
352 &ops.tx_config,
353 None,
354 &[],
355 )
356 .await?
357 .tx_status?
358 }
359 };
360
361 Ok(result)
362}
363
364pub async fn get_trade_accounts_balances<W: Account + Clone>(
365 provider: &Provider,
366 trade_accounts: &[TradeAccountManager<W>],
367 base_asset: &AssetId,
368 quote_asset: &AssetId,
369) -> anyhow::Result<Balances, anyhow::Error> {
370 let contracts = trade_accounts
371 .iter()
372 .map(|trade_account| trade_account.contract.contract_id())
373 .collect::<Vec<_>>();
374 get_contracts_balances(provider, &contracts, base_asset, quote_asset).await
375}
376
377pub async fn wait_for_book_events<W: Account + Clone>(
378 tx_ids: &[Bytes32],
379 trade_account: &TradeAccountManager<W>,
380 order_book: &OrderBookManager<W>,
381 gaspayer_wallet: W,
382) -> anyhow::Result<OrderBookEvents, anyhow::Error> {
383 let mut order_book_events = OrderBookEvents::default();
384 let mut tx_completed = HashSet::new();
385
386 while tx_completed.len() != tx_ids.len() {
387 let provider = gaspayer_wallet.try_provider()?;
388 for order_result in tx_ids.iter() {
389 let result = provider.get_transaction_by_id(order_result).await?;
390
391 if let Some(result) = result {
392 get_order_book_events(
393 order_book,
394 &mut order_book_events,
395 &result.status,
396 )?;
397 match result.status {
398 TxStatus::Success(_) => {
399 tx_completed.insert(*order_result);
400 }
401 TxStatus::Failure(failure) => {
402 let logs = order_book
403 .contract
404 .log_decoder()
405 .decode_logs(&failure.receipts);
406 let logs_trade = trade_account
407 .contract
408 .log_decoder()
409 .decode_logs(&failure.receipts);
410 println!("{logs:#?}");
411 println!("{logs_trade:#?}");
412 panic!("{:#}", failure.reason);
413 }
414 _ => {
415 continue;
416 }
417 }
418 }
419 }
420 }
421
422 Ok(order_book_events)
423}
424
425pub fn get_asset_balance(balances: &HashMap<String, u128>, asset_id: &AssetId) -> u128 {
426 *balances.get(&asset_id.to_string()).unwrap_or(&0)
427}
428
429pub fn get_total_amount(quantity: u64, price: u64, decimals: u64) -> u64 {
430 (quantity * price) / 10u64.pow(decimals as u32)
431}