use crate::{
helpers::get_total_amount,
market_data::{
OrderData,
order_book::MatchedEvent,
},
order_book_deploy::{
OrderCreatedEvent,
OrderMatchedEvent,
},
};
use o2_api_types::primitives::Side;
pub fn assert_orders(orders_events: &[OrderCreatedEvent], orders_state: &[OrderData]) {
assert_eq!(
orders_events
.iter()
.map(|order| (
Side::from(format!("{:?}", order.order_side)),
order.price,
order.quantity,
order.trader_id
))
.collect::<Vec<_>>(),
orders_state
.iter()
.map(|order| (order.side, order.price, order.quantity, order.trader_id))
.collect::<Vec<_>>()
)
}
pub fn assert_matches(
orders_events: &[OrderMatchedEvent],
orders_state: &[MatchedEvent],
base_decimals: u64,
) {
assert_eq!(
orders_events
.iter()
.map(|trade| (
trade.quantity,
trade.price,
get_total_amount(trade.quantity, trade.price, base_decimals)
))
.collect::<Vec<_>>(),
orders_state
.iter()
.map(|trade| (trade.quantity, trade.price, trade.total))
.collect::<Vec<_>>()
)
}
#[cfg(test)]
mod tests {
use crate::{
CallOption,
e2e::{
assert_matches,
assert_orders,
},
helpers::{
create_order_handlers,
fund_trade_accounts,
get_trade_accounts_balances,
send_transactions,
settle_trade_accounts_balances,
setup_order_book,
setup_trade_accounts,
wait_for_book_events,
},
market_data::{
MarketData,
OrderBookAction,
OrderData,
order_book::get_default_orders_test,
},
order_book::Side,
order_book_deploy::{
OrderBookConfigurables,
OrderBookDeployConfig,
},
};
use fuels::{
prelude::*,
test_helpers::{
WalletsConfig,
launch_custom_provider_and_get_wallets,
},
};
#[tokio::test]
#[ignore = "@FIX: this test is been flaky on ci so ignoring for now"]
async fn test_e2e_book() {
let base_asset = AssetId::from([1; 32]);
let quote_asset = AssetId::from([2; 32]);
let initial_balance = 1_000_000_000_000_000u64;
let gas_per_method = Some(1_000_000);
let mut wallets = launch_custom_provider_and_get_wallets(
WalletsConfig::new_multiple_assets(
4,
vec![
AssetConfig {
id: AssetId::default(),
num_coins: 2,
coin_amount: initial_balance,
},
AssetConfig {
id: base_asset,
num_coins: 2,
coin_amount: initial_balance,
},
AssetConfig {
id: quote_asset,
num_coins: 2,
coin_amount: initial_balance,
},
],
),
None,
None,
)
.await
.unwrap();
let deployer_wallet = wallets.pop().unwrap();
let gaspayer_wallet = wallets.pop().unwrap();
let provider = deployer_wallet.provider().clone();
let order_book_configurables = OrderBookConfigurables::default()
.with_MAKER_FEE(0.into())
.unwrap()
.with_TAKER_FEE(0.into())
.unwrap()
.with_MIN_ORDER(0)
.unwrap()
.with_DUST(0)
.unwrap();
let order_book = setup_order_book(
&deployer_wallet,
base_asset,
quote_asset,
&OrderBookDeployConfig::with_configurables(order_book_configurables),
)
.await
.unwrap();
let mut trade_accounts = setup_trade_accounts(
&deployer_wallet,
&[order_book.contract.id()],
&mut wallets,
)
.await
.unwrap();
println!("Order book deployed: {}", order_book.contract.contract_id());
fund_trade_accounts(
&trade_accounts,
base_asset,
2_000_000_000_000u64,
quote_asset,
2_000_000_000_000u64,
)
.await
.unwrap();
let balances = get_trade_accounts_balances(
&provider,
&trade_accounts,
&base_asset,
"e_asset,
)
.await
.unwrap();
let (order_book_state, test_order_book_state) =
get_default_orders_test(base_asset, quote_asset, balances);
let create_orders_handlers = create_order_handlers(
&order_book,
&test_order_book_state.orders,
trade_accounts.iter_mut(),
gas_per_method,
)
.await
.unwrap();
let tx_ids = send_transactions(
create_orders_handlers,
gaspayer_wallet.clone(),
CallOption::AwaitBlock,
)
.await;
let trade_account = trade_accounts.first().unwrap();
let order_book_events = wait_for_book_events(
&tx_ids,
trade_account,
&order_book,
gaspayer_wallet.clone(),
)
.await
.unwrap();
assert_orders(&order_book_events.orders, &test_order_book_state.orders);
assert_matches(
&order_book_events.matches,
&test_order_book_state.matches,
9,
);
settle_trade_accounts_balances(
&gaspayer_wallet,
&order_book,
trade_accounts.iter(),
CallOption::AwaitBlock,
)
.await
.unwrap();
let balances = get_trade_accounts_balances(
&provider,
&trade_accounts,
&base_asset,
"e_asset,
)
.await
.unwrap();
assert_eq!(balances, order_book_state.balances);
}
#[tokio::test]
#[ignore = "@FIX: this test is been flaky on ci so ignoring for now"]
async fn test_e2e_book_multiple_orders() {
let base_asset = AssetId::from([1; 32]);
let quote_asset = AssetId::from([2; 32]);
let initial_balance = 1_000_000_000_000_000u64;
let gas_per_method = Some(30_000_000);
let mut wallets = launch_custom_provider_and_get_wallets(
WalletsConfig::new_multiple_assets(
6,
vec![
AssetConfig {
id: AssetId::zeroed(),
num_coins: 2,
coin_amount: initial_balance,
},
AssetConfig {
id: base_asset,
num_coins: 2,
coin_amount: initial_balance,
},
AssetConfig {
id: quote_asset,
num_coins: 2,
coin_amount: initial_balance,
},
],
),
None,
None,
)
.await
.unwrap();
let deployer_wallet = wallets.pop().unwrap();
let gaspayer_wallet = wallets.pop().unwrap();
let order_book_configurables = OrderBookConfigurables::default()
.with_MAKER_FEE(0.into())
.unwrap()
.with_TAKER_FEE(0.into())
.unwrap()
.with_MIN_ORDER(0)
.unwrap()
.with_DUST(0)
.unwrap();
let order_book = setup_order_book(
&deployer_wallet,
base_asset,
quote_asset,
&OrderBookDeployConfig::with_configurables(order_book_configurables),
)
.await
.unwrap();
let mut trade_accounts = setup_trade_accounts(
&deployer_wallet,
&[order_book.contract.id()],
&mut wallets,
)
.await
.unwrap();
fund_trade_accounts(
&trade_accounts,
base_asset,
2_000_000_000_000u64,
quote_asset,
2_000_000_000_000u64,
)
.await
.unwrap();
let order_actions = vec![
(false, 469_000_000, 115_000_000),
(true, 261_000_000, 113_000_000),
(true, 869_000_000, 193_000_000),
(true, 791_000_000, 137_000_000),
(true, 285_000_000, 182_000_000),
(false, 121_000_000, 136_000_000),
(true, 103_000_000, 126_000_000),
(false, 44_000_000, 174_000_000),
(false, 918_000_000, 190_000_000),
(false, 697_000_000, 149_000_000),
(true, 716_000_000, 126_000_000),
(true, 397_000_000, 177_000_000),
(true, 490_000_000, 117_000_000),
(false, 159_000_000, 191_000_000),
(false, 134_000_000, 173_000_000),
(false, 356_000_000, 127_000_000),
]
.iter()
.enumerate()
.map(|(index, (is_bid, price, quantity))| {
OrderBookAction::CreateOrder(OrderData {
trader_id: trade_accounts
.get(index % trade_accounts.len())
.unwrap()
.identity(),
price: *price,
quantity: *quantity,
side: if *is_bid { Side::Sell } else { Side::Buy },
})
})
.collect::<Vec<OrderBookAction>>();
let order_book_state =
MarketData::generate_orderbook_state(base_asset, quote_asset, &order_actions);
let orders = order_actions
.iter()
.map(|action| match action {
OrderBookAction::CreateOrder(order) => order.clone(),
OrderBookAction::CancelOrder(_) => {
panic!("Unexpected cancel order action")
}
})
.collect::<Vec<OrderData>>();
let create_orders_handlers = create_order_handlers(
&order_book,
&orders,
trade_accounts.iter_mut(),
gas_per_method,
)
.await
.unwrap();
let tx_ids = send_transactions(
create_orders_handlers,
gaspayer_wallet.clone(),
CallOption::AwaitBlock,
)
.await;
let trade_account = trade_accounts.first().unwrap();
let order_book_events =
wait_for_book_events(&tx_ids, trade_account, &order_book, gaspayer_wallet)
.await
.unwrap();
assert_orders(&order_book_events.orders, &orders);
assert_matches(&order_book_events.matches, &order_book_state.trades, 9);
}
}