2023-05-03 23:02:28 -07:00
|
|
|
#![allow(dead_code)]
|
2023-01-30 12:09:47 -08:00
|
|
|
use super::*;
|
2022-03-11 03:59:42 -08:00
|
|
|
|
2023-11-22 14:40:48 -08:00
|
|
|
use anchor_lang::prelude::AccountMeta;
|
2023-02-14 23:42:07 -08:00
|
|
|
use mango_v4::accounts_ix::{Serum3OrderType, Serum3SelfTradeBehavior, Serum3Side};
|
2023-09-13 00:35:10 -07:00
|
|
|
use mango_v4::serum3_cpi::{load_open_orders_bytes, OpenOrdersSlim};
|
2022-08-26 03:45:32 -07:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
struct SerumOrderPlacer {
|
|
|
|
solana: Arc<SolanaCookie>,
|
|
|
|
serum: Arc<SerumCookie>,
|
|
|
|
account: Pubkey,
|
2022-09-07 03:39:21 -07:00
|
|
|
owner: TestKeypair,
|
2022-08-26 03:45:32 -07:00
|
|
|
serum_market: Pubkey,
|
|
|
|
open_orders: Pubkey,
|
|
|
|
next_client_order_id: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SerumOrderPlacer {
|
|
|
|
fn inc_client_order_id(&mut self) -> u64 {
|
|
|
|
let id = self.next_client_order_id;
|
|
|
|
self.next_client_order_id += 1;
|
|
|
|
id
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn find_order_id_for_client_order_id(&self, client_order_id: u64) -> Option<(u128, u64)> {
|
|
|
|
let open_orders = self.serum.load_open_orders(self.open_orders).await;
|
|
|
|
for i in 0..128 {
|
|
|
|
if open_orders.free_slot_bits & (1u128 << i) != 0 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if open_orders.client_order_ids[i] == client_order_id {
|
|
|
|
return Some((open_orders.orders[i], client_order_id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2023-05-03 23:02:28 -07:00
|
|
|
async fn try_bid(
|
|
|
|
&mut self,
|
|
|
|
limit_price: f64,
|
|
|
|
max_base: u64,
|
|
|
|
taker: bool,
|
|
|
|
) -> Result<mango_v4::accounts::Serum3PlaceOrder, TransportError> {
|
2022-08-26 03:45:32 -07:00
|
|
|
let client_order_id = self.inc_client_order_id();
|
2023-05-03 23:02:28 -07:00
|
|
|
let fees = if taker { 0.0004 } else { 0.0 };
|
2022-08-26 03:45:32 -07:00
|
|
|
send_tx(
|
|
|
|
&self.solana,
|
|
|
|
Serum3PlaceOrderInstruction {
|
|
|
|
side: Serum3Side::Bid,
|
|
|
|
limit_price: (limit_price * 100.0 / 10.0) as u64, // in quote_lot (10) per base lot (100)
|
|
|
|
max_base_qty: max_base / 100, // in base lot (100)
|
2023-05-03 23:02:28 -07:00
|
|
|
// 4 bps taker fees added in
|
|
|
|
max_native_quote_qty_including_fees: (limit_price
|
|
|
|
* (max_base as f64)
|
|
|
|
* (1.0 + fees))
|
|
|
|
.ceil() as u64,
|
2022-08-26 03:45:32 -07:00
|
|
|
self_trade_behavior: Serum3SelfTradeBehavior::AbortTransaction,
|
|
|
|
order_type: Serum3OrderType::Limit,
|
|
|
|
client_order_id,
|
|
|
|
limit: 10,
|
|
|
|
account: self.account,
|
2022-09-07 03:39:21 -07:00
|
|
|
owner: self.owner,
|
2022-08-26 03:45:32 -07:00
|
|
|
serum_market: self.serum_market,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
2023-05-03 23:02:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn bid_maker(&mut self, limit_price: f64, max_base: u64) -> Option<(u128, u64)> {
|
|
|
|
self.try_bid(limit_price, max_base, false).await.unwrap();
|
|
|
|
self.find_order_id_for_client_order_id(self.next_client_order_id - 1)
|
2022-08-26 03:45:32 -07:00
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
2023-05-03 23:02:28 -07:00
|
|
|
async fn bid_taker(&mut self, limit_price: f64, max_base: u64) -> Option<(u128, u64)> {
|
|
|
|
self.try_bid(limit_price, max_base, true).await.unwrap();
|
|
|
|
self.find_order_id_for_client_order_id(self.next_client_order_id - 1)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn try_ask(
|
|
|
|
&mut self,
|
|
|
|
limit_price: f64,
|
|
|
|
max_base: u64,
|
|
|
|
) -> Result<mango_v4::accounts::Serum3PlaceOrder, TransportError> {
|
2022-08-26 03:45:32 -07:00
|
|
|
let client_order_id = self.inc_client_order_id();
|
|
|
|
send_tx(
|
|
|
|
&self.solana,
|
|
|
|
Serum3PlaceOrderInstruction {
|
|
|
|
side: Serum3Side::Ask,
|
|
|
|
limit_price: (limit_price * 100.0 / 10.0) as u64, // in quote_lot (10) per base lot (100)
|
|
|
|
max_base_qty: max_base / 100, // in base lot (100)
|
|
|
|
max_native_quote_qty_including_fees: (limit_price * (max_base as f64)) as u64,
|
|
|
|
self_trade_behavior: Serum3SelfTradeBehavior::AbortTransaction,
|
|
|
|
order_type: Serum3OrderType::Limit,
|
|
|
|
client_order_id,
|
|
|
|
limit: 10,
|
|
|
|
account: self.account,
|
2022-09-07 03:39:21 -07:00
|
|
|
owner: self.owner,
|
2022-08-26 03:45:32 -07:00
|
|
|
serum_market: self.serum_market,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
2023-05-03 23:02:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn ask(&mut self, limit_price: f64, max_base: u64) -> Option<(u128, u64)> {
|
|
|
|
self.try_ask(limit_price, max_base).await.unwrap();
|
|
|
|
self.find_order_id_for_client_order_id(self.next_client_order_id - 1)
|
2022-08-26 03:45:32 -07:00
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn cancel(&self, order_id: u128) {
|
|
|
|
let side = {
|
|
|
|
let open_orders = self.serum.load_open_orders(self.open_orders).await;
|
|
|
|
let orders = open_orders.orders;
|
|
|
|
let idx = orders.iter().position(|&v| v == order_id).unwrap();
|
|
|
|
if open_orders.is_bid_bits & (1u128 << idx) == 0 {
|
|
|
|
Serum3Side::Ask
|
|
|
|
} else {
|
|
|
|
Serum3Side::Bid
|
|
|
|
}
|
|
|
|
};
|
|
|
|
send_tx(
|
|
|
|
&self.solana,
|
|
|
|
Serum3CancelOrderInstruction {
|
|
|
|
side,
|
|
|
|
order_id,
|
|
|
|
account: self.account,
|
2022-09-07 03:39:21 -07:00
|
|
|
owner: self.owner,
|
2022-08-26 03:45:32 -07:00
|
|
|
serum_market: self.serum_market,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
2023-12-01 04:36:29 -08:00
|
|
|
async fn cancel_by_client_order_id(&self, client_order_id: u64) {
|
|
|
|
send_tx(
|
|
|
|
&self.solana,
|
|
|
|
Serum3CancelOrderByClientOrderIdInstruction {
|
|
|
|
client_order_id,
|
|
|
|
account: self.account,
|
|
|
|
owner: self.owner,
|
|
|
|
serum_market: self.serum_market,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
2023-09-13 00:35:10 -07:00
|
|
|
async fn cancel_all(&self) {
|
|
|
|
let open_orders = self.serum.load_open_orders(self.open_orders).await;
|
|
|
|
let orders = open_orders.orders;
|
|
|
|
for (idx, order_id) in orders.iter().enumerate() {
|
2023-11-03 03:20:37 -07:00
|
|
|
let mask = 1u128 << idx;
|
|
|
|
if open_orders.free_slot_bits & mask != 0 {
|
2023-09-13 00:35:10 -07:00
|
|
|
continue;
|
|
|
|
}
|
2023-11-03 03:20:37 -07:00
|
|
|
let side = if open_orders.is_bid_bits & mask == 0 {
|
2023-09-13 00:35:10 -07:00
|
|
|
Serum3Side::Ask
|
|
|
|
} else {
|
|
|
|
Serum3Side::Bid
|
|
|
|
};
|
|
|
|
|
|
|
|
send_tx(
|
|
|
|
&self.solana,
|
|
|
|
Serum3CancelOrderInstruction {
|
|
|
|
side,
|
|
|
|
order_id: *order_id,
|
|
|
|
account: self.account,
|
|
|
|
owner: self.owner,
|
|
|
|
serum_market: self.serum_market,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-26 03:45:32 -07:00
|
|
|
async fn settle(&self) {
|
2023-06-15 08:34:56 -07:00
|
|
|
self.settle_v2(true).await
|
2022-08-26 03:45:32 -07:00
|
|
|
}
|
|
|
|
|
2023-03-03 05:04:45 -08:00
|
|
|
async fn settle_v2(&self, fees_to_dao: bool) {
|
|
|
|
send_tx(
|
|
|
|
&self.solana,
|
|
|
|
Serum3SettleFundsV2Instruction {
|
|
|
|
account: self.account,
|
|
|
|
owner: self.owner,
|
|
|
|
serum_market: self.serum_market,
|
|
|
|
fees_to_dao,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
2022-08-26 03:45:32 -07:00
|
|
|
async fn mango_serum_orders(&self) -> Serum3Orders {
|
|
|
|
let account_data = get_mango_account(&self.solana, self.account).await;
|
|
|
|
let orders = account_data
|
|
|
|
.all_serum3_orders()
|
|
|
|
.find(|s| s.open_orders == self.open_orders)
|
|
|
|
.unwrap();
|
|
|
|
orders.clone()
|
|
|
|
}
|
2023-03-03 05:04:45 -08:00
|
|
|
|
|
|
|
async fn _open_orders(&self) -> OpenOrdersSlim {
|
|
|
|
let data = self
|
|
|
|
.solana
|
|
|
|
.get_account_data(self.open_orders)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
OpenOrdersSlim::from_oo(load_open_orders_bytes(&data).unwrap())
|
|
|
|
}
|
2022-08-26 03:45:32 -07:00
|
|
|
}
|
2022-08-26 06:59:47 -07:00
|
|
|
|
2022-03-11 03:59:42 -08:00
|
|
|
#[tokio::test]
|
2022-08-26 03:45:32 -07:00
|
|
|
async fn test_serum_basics() -> Result<(), TransportError> {
|
2022-08-23 05:10:38 -07:00
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(95_000); // Serum3PlaceOrder needs 92.8k
|
|
|
|
let context = test_builder.start_default().await;
|
2022-03-11 03:59:42 -08:00
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
2022-09-07 03:39:21 -07:00
|
|
|
let admin = TestKeypair::new();
|
|
|
|
let owner = context.users[0].key;
|
|
|
|
let payer = context.users[1].key;
|
2022-03-20 23:49:51 -07:00
|
|
|
let mints = &context.mints[0..2];
|
2022-03-11 06:28:27 -08:00
|
|
|
|
2022-03-11 03:59:42 -08:00
|
|
|
//
|
2022-03-11 05:26:42 -08:00
|
|
|
// SETUP: Create a group and an account
|
2022-03-11 03:59:42 -08:00
|
|
|
//
|
|
|
|
|
2022-08-26 06:59:47 -07:00
|
|
|
let GroupWithTokens { group, tokens, .. } = GroupWithTokensConfig {
|
2022-03-20 23:49:51 -07:00
|
|
|
admin,
|
|
|
|
payer,
|
2022-09-12 06:25:50 -07:00
|
|
|
mints: mints.to_vec(),
|
|
|
|
..GroupWithTokensConfig::default()
|
2022-03-20 23:49:51 -07:00
|
|
|
}
|
|
|
|
.create(solana)
|
|
|
|
.await;
|
|
|
|
let base_token = &tokens[0];
|
|
|
|
let quote_token = &tokens[1];
|
2022-03-11 03:59:42 -08:00
|
|
|
|
|
|
|
//
|
2022-03-20 23:49:51 -07:00
|
|
|
// SETUP: Create serum market
|
2022-03-11 03:59:42 -08:00
|
|
|
//
|
2022-03-20 23:49:51 -07:00
|
|
|
let serum_market_cookie = context
|
|
|
|
.serum
|
|
|
|
.list_spot_market(&base_token.mint, "e_token.mint)
|
|
|
|
.await;
|
2022-03-11 03:59:42 -08:00
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Register a serum market
|
|
|
|
//
|
2022-03-11 05:26:42 -08:00
|
|
|
let serum_market = send_tx(
|
2022-03-11 03:59:42 -08:00
|
|
|
solana,
|
2022-03-18 05:42:20 -07:00
|
|
|
Serum3RegisterMarketInstruction {
|
2022-03-11 03:59:42 -08:00
|
|
|
group,
|
|
|
|
admin,
|
2022-03-11 06:28:27 -08:00
|
|
|
serum_program: context.serum.program_id,
|
|
|
|
serum_market_external: serum_market_cookie.market,
|
2022-03-14 05:47:34 -07:00
|
|
|
market_index: 0,
|
2022-03-30 03:24:07 -07:00
|
|
|
base_bank: base_token.bank,
|
|
|
|
quote_bank: quote_token.bank,
|
2022-03-11 03:59:42 -08:00
|
|
|
payer,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
2022-03-11 05:26:42 -08:00
|
|
|
.unwrap()
|
|
|
|
.serum_market;
|
|
|
|
|
2022-08-26 03:45:32 -07:00
|
|
|
//
|
|
|
|
// SETUP: Create account
|
|
|
|
//
|
|
|
|
let deposit_amount = 1000;
|
|
|
|
let account = create_funded_account(
|
|
|
|
&solana,
|
|
|
|
group,
|
|
|
|
owner,
|
|
|
|
0,
|
|
|
|
&context.users[1],
|
|
|
|
mints,
|
|
|
|
deposit_amount,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
2022-03-11 05:26:42 -08:00
|
|
|
//
|
|
|
|
// TEST: Create an open orders account
|
|
|
|
//
|
2022-03-11 08:49:40 -08:00
|
|
|
let open_orders = send_tx(
|
2022-03-11 05:26:42 -08:00
|
|
|
solana,
|
2022-03-18 05:42:20 -07:00
|
|
|
Serum3CreateOpenOrdersInstruction {
|
2022-03-11 05:26:42 -08:00
|
|
|
account,
|
|
|
|
serum_market,
|
|
|
|
owner,
|
|
|
|
payer,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
2022-03-11 08:49:40 -08:00
|
|
|
.unwrap()
|
|
|
|
.open_orders;
|
|
|
|
|
2022-07-25 07:07:53 -07:00
|
|
|
let account_data = get_mango_account(solana, account).await;
|
2022-03-11 08:49:40 -08:00
|
|
|
assert_eq!(
|
|
|
|
account_data
|
2022-08-18 04:45:31 -07:00
|
|
|
.active_serum3_orders()
|
2022-03-11 08:49:40 -08:00
|
|
|
.map(|v| (v.open_orders, v.market_index))
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
[(open_orders, 0)]
|
|
|
|
);
|
2022-03-11 03:59:42 -08:00
|
|
|
|
2022-08-26 03:45:32 -07:00
|
|
|
let mut order_placer = SerumOrderPlacer {
|
|
|
|
solana: solana.clone(),
|
|
|
|
serum: context.serum.clone(),
|
|
|
|
account,
|
|
|
|
owner: owner.clone(),
|
|
|
|
serum_market,
|
|
|
|
open_orders,
|
|
|
|
next_client_order_id: 0,
|
|
|
|
};
|
|
|
|
|
2022-03-14 09:21:16 -07:00
|
|
|
//
|
|
|
|
// TEST: Place an order
|
|
|
|
//
|
2023-12-05 06:39:24 -08:00
|
|
|
let (order_id, _) = order_placer.bid_maker(0.9, 100).await.unwrap();
|
2022-08-24 05:08:08 -07:00
|
|
|
check_prev_instruction_post_health(&solana, account).await;
|
|
|
|
|
2022-03-20 23:49:51 -07:00
|
|
|
let native0 = account_position(solana, account, base_token.bank).await;
|
|
|
|
let native1 = account_position(solana, account, quote_token.bank).await;
|
2022-03-15 06:44:47 -07:00
|
|
|
assert_eq!(native0, 1000);
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(native1, 910);
|
2022-03-15 06:44:47 -07:00
|
|
|
|
2022-08-26 03:45:32 -07:00
|
|
|
let account_data = get_mango_account(solana, account).await;
|
2023-07-03 05:09:11 -07:00
|
|
|
assert_eq!(
|
|
|
|
account_data
|
|
|
|
.token_position_by_raw_index(0)
|
|
|
|
.unwrap()
|
|
|
|
.in_use_count,
|
|
|
|
1
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
account_data
|
|
|
|
.token_position_by_raw_index(1)
|
|
|
|
.unwrap()
|
|
|
|
.in_use_count,
|
|
|
|
1
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
account_data
|
|
|
|
.token_position_by_raw_index(2)
|
|
|
|
.unwrap()
|
|
|
|
.in_use_count,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
let serum_orders = account_data.serum3_orders_by_raw_index(0).unwrap();
|
2022-08-26 03:45:32 -07:00
|
|
|
assert_eq!(serum_orders.base_borrows_without_fee, 0);
|
|
|
|
assert_eq!(serum_orders.quote_borrows_without_fee, 0);
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(serum_orders.potential_base_tokens, 100);
|
|
|
|
assert_eq!(serum_orders.potential_quote_tokens, 90);
|
2023-11-02 02:40:31 -07:00
|
|
|
|
|
|
|
let base_bank = solana.get_account::<Bank>(base_token.bank).await;
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(base_bank.potential_serum_tokens, 100);
|
2023-11-02 02:40:31 -07:00
|
|
|
let quote_bank = solana.get_account::<Bank>(quote_token.bank).await;
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(quote_bank.potential_serum_tokens, 90);
|
2022-08-26 03:45:32 -07:00
|
|
|
|
2022-03-19 04:11:56 -07:00
|
|
|
assert!(order_id != 0);
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Cancel the order
|
|
|
|
//
|
2022-08-26 03:45:32 -07:00
|
|
|
order_placer.cancel(order_id).await;
|
2022-03-19 04:11:56 -07:00
|
|
|
|
2023-12-01 04:36:29 -08:00
|
|
|
//
|
|
|
|
// TEST: Cancel order by client order id
|
|
|
|
//
|
|
|
|
let (_, _) = order_placer.bid_maker(1.0, 100).await.unwrap();
|
|
|
|
order_placer
|
|
|
|
.cancel_by_client_order_id(order_placer.next_client_order_id - 1)
|
|
|
|
.await;
|
|
|
|
|
2022-03-19 04:11:56 -07:00
|
|
|
//
|
|
|
|
// TEST: Settle, moving the freed up funds back
|
|
|
|
//
|
2022-08-26 03:45:32 -07:00
|
|
|
order_placer.settle().await;
|
|
|
|
|
|
|
|
let native0 = account_position(solana, account, base_token.bank).await;
|
|
|
|
let native1 = account_position(solana, account, quote_token.bank).await;
|
|
|
|
assert_eq!(native0, 1000);
|
|
|
|
assert_eq!(native1, 1000);
|
|
|
|
|
2023-11-02 02:40:31 -07:00
|
|
|
let account_data = get_mango_account(solana, account).await;
|
|
|
|
let serum_orders = account_data.serum3_orders_by_raw_index(0).unwrap();
|
|
|
|
assert_eq!(serum_orders.base_borrows_without_fee, 0);
|
|
|
|
assert_eq!(serum_orders.quote_borrows_without_fee, 0);
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(serum_orders.potential_base_tokens, 0);
|
|
|
|
assert_eq!(serum_orders.potential_quote_tokens, 0);
|
2023-11-02 02:40:31 -07:00
|
|
|
|
|
|
|
let base_bank = solana.get_account::<Bank>(base_token.bank).await;
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(base_bank.potential_serum_tokens, 0);
|
2023-11-02 02:40:31 -07:00
|
|
|
let quote_bank = solana.get_account::<Bank>(quote_token.bank).await;
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(quote_bank.potential_serum_tokens, 0);
|
2023-11-02 02:40:31 -07:00
|
|
|
|
2022-08-26 03:45:32 -07:00
|
|
|
// Process events such that the OutEvent deactivates the closed order on open_orders
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(&serum_market_cookie, &[open_orders])
|
|
|
|
.await;
|
|
|
|
|
|
|
|
// close oo account
|
2022-03-18 07:58:39 -07:00
|
|
|
send_tx(
|
|
|
|
solana,
|
2022-08-26 03:45:32 -07:00
|
|
|
Serum3CloseOpenOrdersInstruction {
|
2022-03-18 07:58:39 -07:00
|
|
|
account,
|
|
|
|
serum_market,
|
2022-08-26 03:45:32 -07:00
|
|
|
owner,
|
|
|
|
sol_destination: payer.pubkey(),
|
2022-03-18 07:58:39 -07:00
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2022-08-30 01:28:53 -07:00
|
|
|
let account_data = get_mango_account(solana, account).await;
|
2023-07-03 05:09:11 -07:00
|
|
|
assert_eq!(
|
|
|
|
account_data
|
|
|
|
.token_position_by_raw_index(0)
|
|
|
|
.unwrap()
|
|
|
|
.in_use_count,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
account_data
|
|
|
|
.token_position_by_raw_index(1)
|
|
|
|
.unwrap()
|
|
|
|
.in_use_count,
|
|
|
|
0
|
|
|
|
);
|
2022-08-30 01:28:53 -07:00
|
|
|
|
2022-06-09 09:27:31 -07:00
|
|
|
// deregister serum3 market
|
|
|
|
send_tx(
|
|
|
|
solana,
|
|
|
|
Serum3DeregisterMarketInstruction {
|
|
|
|
group,
|
|
|
|
admin,
|
|
|
|
serum_market_external: serum_market_cookie.market,
|
|
|
|
sol_destination: payer.pubkey(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2022-03-11 03:59:42 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
2022-08-26 03:45:32 -07:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_loan_origination_fees() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
2023-08-07 07:15:45 -07:00
|
|
|
test_builder.test().set_compute_max_units(100_000); // Serum3PlaceOrder needs 95.1k
|
2022-08-26 03:45:32 -07:00
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
|
|
|
//
|
2023-03-03 05:04:45 -08:00
|
|
|
// SETUP: Create a group, accounts, market etc
|
2022-08-26 03:45:32 -07:00
|
|
|
//
|
|
|
|
let deposit_amount = 180000;
|
2023-03-03 05:04:45 -08:00
|
|
|
let CommonSetup {
|
|
|
|
serum_market_cookie,
|
2023-03-20 03:18:11 -07:00
|
|
|
quote_token,
|
|
|
|
base_token,
|
2023-03-03 05:04:45 -08:00
|
|
|
mut order_placer,
|
|
|
|
mut order_placer2,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
2023-03-20 03:18:11 -07:00
|
|
|
let quote_bank = quote_token.bank;
|
|
|
|
let base_bank = base_token.bank;
|
2023-03-03 05:04:45 -08:00
|
|
|
let account = order_placer.account;
|
|
|
|
let account2 = order_placer2.account;
|
2022-08-26 03:45:32 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Placing and canceling an order does not take loan origination fees even if borrows are needed
|
|
|
|
//
|
|
|
|
{
|
2023-05-03 23:02:28 -07:00
|
|
|
let (bid_order_id, _) = order_placer.bid_maker(1.0, 200000).await.unwrap();
|
2022-08-26 03:45:32 -07:00
|
|
|
let (ask_order_id, _) = order_placer.ask(2.0, 200000).await.unwrap();
|
|
|
|
|
|
|
|
let o = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(o.base_borrows_without_fee, 19999); // rounded
|
|
|
|
assert_eq!(o.quote_borrows_without_fee, 19999);
|
|
|
|
|
|
|
|
order_placer.cancel(bid_order_id).await;
|
|
|
|
order_placer.cancel(ask_order_id).await;
|
|
|
|
|
|
|
|
let o = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(o.base_borrows_without_fee, 19999); // unchanged
|
|
|
|
assert_eq!(o.quote_borrows_without_fee, 19999);
|
|
|
|
|
|
|
|
// placing new, slightly larger orders increases the borrow_without_fee amount only by a small amount
|
2023-05-03 23:02:28 -07:00
|
|
|
let (bid_order_id, _) = order_placer.bid_maker(1.0, 210000).await.unwrap();
|
2022-08-26 03:45:32 -07:00
|
|
|
let (ask_order_id, _) = order_placer.ask(2.0, 300000).await.unwrap();
|
|
|
|
|
|
|
|
let o = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(o.base_borrows_without_fee, 119998); // rounded
|
|
|
|
assert_eq!(o.quote_borrows_without_fee, 29998);
|
|
|
|
|
|
|
|
order_placer.cancel(bid_order_id).await;
|
|
|
|
order_placer.cancel(ask_order_id).await;
|
|
|
|
|
|
|
|
// returns all the funds
|
|
|
|
order_placer.settle().await;
|
|
|
|
|
|
|
|
let o = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(o.base_borrows_without_fee, 0);
|
|
|
|
assert_eq!(o.quote_borrows_without_fee, 0);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
account_position(solana, account, quote_bank).await,
|
|
|
|
deposit_amount as i64
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
account_position(solana, account, base_bank).await,
|
|
|
|
deposit_amount as i64
|
|
|
|
);
|
|
|
|
|
|
|
|
// consume all the out events from the cancels
|
|
|
|
context
|
|
|
|
.serum
|
2023-03-03 05:04:45 -08:00
|
|
|
.consume_spot_events(&serum_market_cookie, &[order_placer.open_orders])
|
2022-08-26 03:45:32 -07:00
|
|
|
.await;
|
|
|
|
}
|
|
|
|
|
2022-11-16 02:50:40 -08:00
|
|
|
let without_serum_taker_fee = |amount: i64| (amount as f64 * (1.0 - 0.0004)).trunc() as i64;
|
2023-02-22 03:04:21 -08:00
|
|
|
let serum_maker_rebate = |amount: i64| (amount as f64 * 0.0002).floor() as i64;
|
|
|
|
let serum_fee = |amount: i64| (amount as f64 * 0.0002).trunc() as i64;
|
2022-08-26 03:45:32 -07:00
|
|
|
let loan_origination_fee = |amount: i64| (amount as f64 * 0.0005).trunc() as i64;
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Order execution and settling charges borrow fee
|
|
|
|
//
|
|
|
|
{
|
|
|
|
let deposit_amount = deposit_amount as i64;
|
|
|
|
let bid_amount = 200000;
|
|
|
|
let ask_amount = 210000;
|
|
|
|
let fill_amount = 200000;
|
2023-02-22 03:04:21 -08:00
|
|
|
let quote_fees1 = solana
|
|
|
|
.get_account::<Bank>(quote_bank)
|
|
|
|
.await
|
|
|
|
.collected_fees_native;
|
2022-08-26 03:45:32 -07:00
|
|
|
|
|
|
|
// account2 has an order on the book
|
2023-05-03 23:02:28 -07:00
|
|
|
order_placer2
|
|
|
|
.bid_maker(1.0, bid_amount as u64)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2022-08-26 03:45:32 -07:00
|
|
|
|
|
|
|
// account takes
|
|
|
|
order_placer.ask(1.0, ask_amount as u64).await.unwrap();
|
|
|
|
order_placer.settle().await;
|
|
|
|
|
|
|
|
let o = order_placer.mango_serum_orders().await;
|
|
|
|
// parts of the order ended up on the book an may cause loan origination fees later
|
|
|
|
assert_eq!(
|
|
|
|
o.base_borrows_without_fee,
|
|
|
|
(ask_amount - fill_amount) as u64
|
|
|
|
);
|
|
|
|
assert_eq!(o.quote_borrows_without_fee, 0);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
account_position(solana, account, quote_bank).await,
|
|
|
|
deposit_amount + without_serum_taker_fee(fill_amount)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
account_position(solana, account, base_bank).await,
|
|
|
|
deposit_amount - ask_amount - loan_origination_fee(fill_amount - deposit_amount)
|
|
|
|
);
|
|
|
|
|
2023-02-22 03:04:21 -08:00
|
|
|
// Serum referrer rebates only accrue once the events are executed
|
|
|
|
let quote_fees2 = solana
|
|
|
|
.get_account::<Bank>(quote_bank)
|
|
|
|
.await
|
|
|
|
.collected_fees_native;
|
|
|
|
assert!(assert_equal(quote_fees2 - quote_fees1, 0.0, 0.1));
|
|
|
|
|
2022-08-26 03:45:32 -07:00
|
|
|
// check account2 balances too
|
|
|
|
context
|
|
|
|
.serum
|
2023-03-03 05:04:45 -08:00
|
|
|
.consume_spot_events(
|
|
|
|
&serum_market_cookie,
|
|
|
|
&[order_placer.open_orders, order_placer2.open_orders],
|
|
|
|
)
|
2022-08-26 03:45:32 -07:00
|
|
|
.await;
|
|
|
|
order_placer2.settle().await;
|
|
|
|
|
|
|
|
let o = order_placer2.mango_serum_orders().await;
|
|
|
|
assert_eq!(o.base_borrows_without_fee, 0);
|
|
|
|
assert_eq!(o.quote_borrows_without_fee, 0);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
account_position(solana, account2, base_bank).await,
|
|
|
|
deposit_amount + fill_amount
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
account_position(solana, account2, quote_bank).await,
|
|
|
|
deposit_amount - fill_amount - loan_origination_fee(fill_amount - deposit_amount)
|
2023-02-22 03:04:21 -08:00
|
|
|
+ (serum_maker_rebate(fill_amount) - 1) // unclear where the -1 comes from?
|
|
|
|
);
|
|
|
|
|
|
|
|
// Serum referrer rebates accrue on the taker side
|
|
|
|
let quote_fees3 = solana
|
|
|
|
.get_account::<Bank>(quote_bank)
|
|
|
|
.await
|
|
|
|
.collected_fees_native;
|
|
|
|
assert!(assert_equal(
|
|
|
|
quote_fees3 - quote_fees1,
|
|
|
|
loan_origination_fee(fill_amount - deposit_amount) as f64,
|
|
|
|
0.1
|
|
|
|
));
|
|
|
|
|
|
|
|
order_placer.settle().await;
|
|
|
|
|
|
|
|
// Now rebates got collected as Mango fees, but user balances are unchanged
|
|
|
|
let quote_fees4 = solana
|
|
|
|
.get_account::<Bank>(quote_bank)
|
|
|
|
.await
|
|
|
|
.collected_fees_native;
|
|
|
|
assert!(assert_equal(
|
|
|
|
quote_fees4 - quote_fees3,
|
|
|
|
serum_fee(fill_amount) as f64,
|
|
|
|
0.1
|
|
|
|
));
|
|
|
|
|
2023-02-28 00:48:15 -08:00
|
|
|
let account_data = solana.get_account::<MangoAccount>(account).await;
|
|
|
|
assert_eq!(
|
|
|
|
account_data.buyback_fees_accrued_current,
|
2023-06-19 05:48:08 -07:00
|
|
|
serum_maker_rebate(fill_amount) as u64
|
2023-02-28 00:48:15 -08:00
|
|
|
);
|
|
|
|
|
2023-02-22 03:04:21 -08:00
|
|
|
assert_eq!(
|
|
|
|
account_position(solana, account, quote_bank).await,
|
|
|
|
deposit_amount + without_serum_taker_fee(fill_amount)
|
2022-08-26 03:45:32 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2023-03-03 05:04:45 -08:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_settle_v1() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(95_000); // Serum3PlaceOrder needs 92.8k
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group, accounts, market etc
|
|
|
|
//
|
|
|
|
let deposit_amount = 160000;
|
|
|
|
let CommonSetup {
|
|
|
|
serum_market_cookie,
|
2023-03-20 03:18:11 -07:00
|
|
|
quote_token,
|
|
|
|
base_token,
|
2023-03-03 05:04:45 -08:00
|
|
|
mut order_placer,
|
|
|
|
mut order_placer2,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
2023-03-20 03:18:11 -07:00
|
|
|
let quote_bank = quote_token.bank;
|
|
|
|
let base_bank = base_token.bank;
|
2023-03-03 05:04:45 -08:00
|
|
|
let account = order_placer.account;
|
|
|
|
let account2 = order_placer2.account;
|
|
|
|
|
|
|
|
let serum_taker_fee = |amount: i64| (amount as f64 * 0.0004).trunc() as i64;
|
|
|
|
let serum_maker_rebate = |amount: i64| (amount as f64 * 0.0002).floor() as i64;
|
|
|
|
let serum_referrer_fee = |amount: i64| (amount as f64 * 0.0002).trunc() as i64;
|
|
|
|
let loan_origination_fee = |amount: i64| (amount as f64 * 0.0005).trunc() as i64;
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Use v1 serum3_settle_funds
|
|
|
|
//
|
|
|
|
let deposit_amount = deposit_amount as i64;
|
|
|
|
let amount = 200000;
|
|
|
|
let quote_fees_start = solana
|
|
|
|
.get_account::<Bank>(quote_bank)
|
|
|
|
.await
|
|
|
|
.collected_fees_native;
|
|
|
|
let quote_start = account_position(solana, account, quote_bank).await;
|
|
|
|
let quote2_start = account_position(solana, account2, quote_bank).await;
|
|
|
|
let base_start = account_position(solana, account, base_bank).await;
|
|
|
|
let base2_start = account_position(solana, account2, base_bank).await;
|
|
|
|
|
|
|
|
// account2 has an order on the book, account takes
|
2023-05-03 23:02:28 -07:00
|
|
|
order_placer2.bid_maker(1.0, amount as u64).await.unwrap();
|
2023-03-03 05:04:45 -08:00
|
|
|
order_placer.ask(1.0, amount as u64).await.unwrap();
|
|
|
|
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(
|
|
|
|
&serum_market_cookie,
|
|
|
|
&[order_placer.open_orders, order_placer2.open_orders],
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
order_placer.settle().await;
|
|
|
|
order_placer2.settle().await;
|
|
|
|
|
|
|
|
let quote_end = account_position(solana, account, quote_bank).await;
|
|
|
|
let quote2_end = account_position(solana, account2, quote_bank).await;
|
|
|
|
let base_end = account_position(solana, account, base_bank).await;
|
|
|
|
let base2_end = account_position(solana, account2, base_bank).await;
|
|
|
|
|
|
|
|
let lof = loan_origination_fee(amount - deposit_amount);
|
|
|
|
assert_eq!(base_start - amount - lof, base_end);
|
|
|
|
assert_eq!(base2_start + amount, base2_end);
|
|
|
|
assert_eq!(quote_start + amount - serum_taker_fee(amount), quote_end);
|
|
|
|
assert_eq!(
|
|
|
|
quote2_start - amount + serum_maker_rebate(amount) - lof - 1,
|
|
|
|
quote2_end
|
|
|
|
);
|
|
|
|
|
|
|
|
let quote_fees_end = solana
|
|
|
|
.get_account::<Bank>(quote_bank)
|
|
|
|
.await
|
|
|
|
.collected_fees_native;
|
|
|
|
assert!(assert_equal(
|
|
|
|
quote_fees_end - quote_fees_start,
|
|
|
|
(lof + serum_referrer_fee(amount)) as f64,
|
|
|
|
0.1
|
|
|
|
));
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_settle_v2_to_dao() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(95_000); // Serum3PlaceOrder needs 92.8k
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group, accounts, market etc
|
|
|
|
//
|
|
|
|
let deposit_amount = 160000;
|
|
|
|
let CommonSetup {
|
2023-03-20 03:18:11 -07:00
|
|
|
group_with_tokens,
|
2023-03-03 05:04:45 -08:00
|
|
|
serum_market_cookie,
|
2023-03-20 03:18:11 -07:00
|
|
|
quote_token,
|
|
|
|
base_token,
|
2023-03-03 05:04:45 -08:00
|
|
|
mut order_placer,
|
|
|
|
mut order_placer2,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
2023-03-20 03:18:11 -07:00
|
|
|
let quote_bank = quote_token.bank;
|
|
|
|
let base_bank = base_token.bank;
|
2023-03-03 05:04:45 -08:00
|
|
|
let account = order_placer.account;
|
|
|
|
let account2 = order_placer2.account;
|
|
|
|
|
2023-03-20 03:18:11 -07:00
|
|
|
// Change the quote price to verify that the current value of the serum quote token
|
|
|
|
// is added to the buyback fees amount
|
|
|
|
set_bank_stub_oracle_price(
|
|
|
|
solana,
|
|
|
|
group_with_tokens.group,
|
|
|
|
"e_token,
|
|
|
|
group_with_tokens.admin,
|
|
|
|
2.0,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
2023-03-03 05:04:45 -08:00
|
|
|
let serum_taker_fee = |amount: i64| (amount as f64 * 0.0004).trunc() as i64;
|
|
|
|
let serum_maker_rebate = |amount: i64| (amount as f64 * 0.0002).floor() as i64;
|
|
|
|
let serum_referrer_fee = |amount: i64| (amount as f64 * 0.0002).trunc() as i64;
|
|
|
|
let loan_origination_fee = |amount: i64| (amount as f64 * 0.0005).trunc() as i64;
|
|
|
|
|
|
|
|
//
|
2023-03-20 03:18:11 -07:00
|
|
|
// TEST: Use v2 serum3_settle_funds
|
2023-03-03 05:04:45 -08:00
|
|
|
//
|
|
|
|
let deposit_amount = deposit_amount as i64;
|
|
|
|
let amount = 200000;
|
|
|
|
let quote_fees_start = solana
|
|
|
|
.get_account::<Bank>(quote_bank)
|
|
|
|
.await
|
|
|
|
.collected_fees_native;
|
|
|
|
let quote_start = account_position(solana, account, quote_bank).await;
|
|
|
|
let quote2_start = account_position(solana, account2, quote_bank).await;
|
|
|
|
let base_start = account_position(solana, account, base_bank).await;
|
|
|
|
let base2_start = account_position(solana, account2, base_bank).await;
|
|
|
|
|
|
|
|
// account2 has an order on the book, account takes
|
2023-05-03 23:02:28 -07:00
|
|
|
order_placer2.bid_maker(1.0, amount as u64).await.unwrap();
|
2023-03-03 05:04:45 -08:00
|
|
|
order_placer.ask(1.0, amount as u64).await.unwrap();
|
|
|
|
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(
|
|
|
|
&serum_market_cookie,
|
|
|
|
&[order_placer.open_orders, order_placer2.open_orders],
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
order_placer.settle_v2(true).await;
|
|
|
|
order_placer2.settle_v2(true).await;
|
|
|
|
|
|
|
|
let quote_end = account_position(solana, account, quote_bank).await;
|
|
|
|
let quote2_end = account_position(solana, account2, quote_bank).await;
|
|
|
|
let base_end = account_position(solana, account, base_bank).await;
|
|
|
|
let base2_end = account_position(solana, account2, base_bank).await;
|
|
|
|
|
|
|
|
let lof = loan_origination_fee(amount - deposit_amount);
|
|
|
|
assert_eq!(base_start - amount - lof, base_end);
|
|
|
|
assert_eq!(base2_start + amount, base2_end);
|
|
|
|
assert_eq!(quote_start + amount - serum_taker_fee(amount), quote_end);
|
|
|
|
assert_eq!(
|
|
|
|
quote2_start - amount + serum_maker_rebate(amount) - lof - 1,
|
|
|
|
quote2_end
|
|
|
|
);
|
|
|
|
|
|
|
|
let quote_fees_end = solana
|
|
|
|
.get_account::<Bank>(quote_bank)
|
|
|
|
.await
|
|
|
|
.collected_fees_native;
|
|
|
|
assert!(assert_equal(
|
|
|
|
quote_fees_end - quote_fees_start,
|
|
|
|
(lof + serum_referrer_fee(amount)) as f64,
|
|
|
|
0.1
|
|
|
|
));
|
|
|
|
|
2023-03-20 03:18:11 -07:00
|
|
|
let account_data = solana.get_account::<MangoAccount>(account).await;
|
|
|
|
assert_eq!(
|
|
|
|
account_data.buyback_fees_accrued_current,
|
|
|
|
(serum_maker_rebate(amount) * 2) as u64 // *2 because that's the quote price and this number is in $
|
|
|
|
);
|
|
|
|
let account2_data = solana.get_account::<MangoAccount>(account2).await;
|
|
|
|
assert_eq!(account2_data.buyback_fees_accrued_current, 0);
|
|
|
|
|
2023-03-03 05:04:45 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_settle_v2_to_account() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(95_000); // Serum3PlaceOrder needs 92.8k
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group, accounts, market etc
|
|
|
|
//
|
|
|
|
let deposit_amount = 160000;
|
|
|
|
let CommonSetup {
|
|
|
|
serum_market_cookie,
|
2023-03-20 03:18:11 -07:00
|
|
|
quote_token,
|
|
|
|
base_token,
|
2023-03-03 05:04:45 -08:00
|
|
|
mut order_placer,
|
|
|
|
mut order_placer2,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
2023-03-20 03:18:11 -07:00
|
|
|
let quote_bank = quote_token.bank;
|
|
|
|
let base_bank = base_token.bank;
|
2023-03-03 05:04:45 -08:00
|
|
|
let account = order_placer.account;
|
|
|
|
let account2 = order_placer2.account;
|
|
|
|
|
|
|
|
let serum_taker_fee = |amount: i64| (amount as f64 * 0.0004).trunc() as i64;
|
|
|
|
let serum_maker_rebate = |amount: i64| (amount as f64 * 0.0002).floor() as i64;
|
|
|
|
let serum_referrer_fee = |amount: i64| (amount as f64 * 0.0002).trunc() as i64;
|
|
|
|
let loan_origination_fee = |amount: i64| (amount as f64 * 0.0005).trunc() as i64;
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Use v1 serum3_settle_funds
|
|
|
|
//
|
|
|
|
let deposit_amount = deposit_amount as i64;
|
|
|
|
let amount = 200000;
|
|
|
|
let quote_fees_start = solana
|
|
|
|
.get_account::<Bank>(quote_bank)
|
|
|
|
.await
|
|
|
|
.collected_fees_native;
|
|
|
|
let quote_start = account_position(solana, account, quote_bank).await;
|
|
|
|
let quote2_start = account_position(solana, account2, quote_bank).await;
|
|
|
|
let base_start = account_position(solana, account, base_bank).await;
|
|
|
|
let base2_start = account_position(solana, account2, base_bank).await;
|
|
|
|
|
|
|
|
// account2 has an order on the book, account takes
|
2023-05-03 23:02:28 -07:00
|
|
|
order_placer2.bid_maker(1.0, amount as u64).await.unwrap();
|
2023-03-03 05:04:45 -08:00
|
|
|
order_placer.ask(1.0, amount as u64).await.unwrap();
|
|
|
|
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(
|
|
|
|
&serum_market_cookie,
|
|
|
|
&[order_placer.open_orders, order_placer2.open_orders],
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
order_placer.settle_v2(false).await;
|
|
|
|
order_placer2.settle_v2(false).await;
|
|
|
|
|
|
|
|
let quote_end = account_position(solana, account, quote_bank).await;
|
|
|
|
let quote2_end = account_position(solana, account2, quote_bank).await;
|
|
|
|
let base_end = account_position(solana, account, base_bank).await;
|
|
|
|
let base2_end = account_position(solana, account2, base_bank).await;
|
|
|
|
|
|
|
|
let lof = loan_origination_fee(amount - deposit_amount);
|
|
|
|
assert_eq!(base_start - amount - lof, base_end);
|
|
|
|
assert_eq!(base2_start + amount, base2_end);
|
|
|
|
assert_eq!(
|
|
|
|
quote_start + amount - serum_taker_fee(amount) + serum_referrer_fee(amount),
|
|
|
|
quote_end
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
quote2_start - amount + serum_maker_rebate(amount) - lof - 1,
|
|
|
|
quote2_end
|
|
|
|
);
|
|
|
|
|
|
|
|
let quote_fees_end = solana
|
|
|
|
.get_account::<Bank>(quote_bank)
|
|
|
|
.await
|
|
|
|
.collected_fees_native;
|
|
|
|
assert!(assert_equal(
|
|
|
|
quote_fees_end - quote_fees_start,
|
|
|
|
lof as f64,
|
|
|
|
0.1
|
|
|
|
));
|
|
|
|
|
2023-03-20 03:18:11 -07:00
|
|
|
let account_data = solana.get_account::<MangoAccount>(account).await;
|
|
|
|
assert_eq!(account_data.buyback_fees_accrued_current, 0);
|
|
|
|
let account2_data = solana.get_account::<MangoAccount>(account2).await;
|
|
|
|
assert_eq!(account2_data.buyback_fees_accrued_current, 0);
|
|
|
|
|
2023-03-03 05:04:45 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-05-03 23:02:28 -07:00
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_reduce_only_borrows() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(95_000); // Serum3PlaceOrder needs 92.8k
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group, accounts, market etc
|
|
|
|
//
|
|
|
|
let deposit_amount = 1000;
|
|
|
|
let CommonSetup {
|
|
|
|
group_with_tokens,
|
|
|
|
base_token,
|
|
|
|
mut order_placer,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
|
|
|
|
|
|
|
send_tx(
|
|
|
|
solana,
|
|
|
|
TokenMakeReduceOnly {
|
|
|
|
group: group_with_tokens.group,
|
|
|
|
admin: group_with_tokens.admin,
|
|
|
|
mint: base_token.mint.pubkey,
|
|
|
|
reduce_only: 2,
|
|
|
|
force_close: false,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Cannot borrow tokens when bank is reduce only
|
|
|
|
//
|
|
|
|
|
|
|
|
let err = order_placer.try_ask(1.0, 1100).await;
|
|
|
|
assert_mango_error(&err, MangoError::TokenInReduceOnlyMode.into(), "".into());
|
|
|
|
|
|
|
|
order_placer.try_ask(0.5, 500).await.unwrap();
|
|
|
|
|
|
|
|
let err = order_placer.try_ask(1.0, 600).await;
|
|
|
|
assert_mango_error(&err, MangoError::TokenInReduceOnlyMode.into(), "".into());
|
|
|
|
|
|
|
|
order_placer.try_ask(2.0, 500).await.unwrap();
|
|
|
|
|
|
|
|
let err = order_placer.try_ask(1.0, 100).await;
|
|
|
|
assert_mango_error(&err, MangoError::TokenInReduceOnlyMode.into(), "".into());
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_reduce_only_deposits1() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(95_000); // Serum3PlaceOrder needs 92.8k
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group, accounts, market etc
|
|
|
|
//
|
|
|
|
let deposit_amount = 1000;
|
|
|
|
let CommonSetup {
|
|
|
|
group_with_tokens,
|
|
|
|
base_token,
|
|
|
|
mut order_placer,
|
|
|
|
mut order_placer2,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
|
|
|
|
|
|
|
send_tx(
|
|
|
|
solana,
|
|
|
|
TokenMakeReduceOnly {
|
|
|
|
group: group_with_tokens.group,
|
|
|
|
admin: group_with_tokens.admin,
|
|
|
|
mint: base_token.mint.pubkey,
|
|
|
|
reduce_only: 1,
|
|
|
|
force_close: false,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Cannot buy tokens when deposits are already >0
|
|
|
|
//
|
|
|
|
|
|
|
|
// fails to place on the book
|
|
|
|
let err = order_placer.try_bid(1.0, 1000, false).await;
|
|
|
|
assert_mango_error(&err, MangoError::TokenInReduceOnlyMode.into(), "".into());
|
|
|
|
|
|
|
|
// also fails as a taker order
|
|
|
|
order_placer2.ask(1.0, 500).await.unwrap();
|
|
|
|
let err = order_placer.try_bid(1.0, 100, true).await;
|
|
|
|
assert_mango_error(&err, MangoError::TokenInReduceOnlyMode.into(), "".into());
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_reduce_only_deposits2() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(95_000); // Serum3PlaceOrder needs 92.8k
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group, accounts, market etc
|
|
|
|
//
|
|
|
|
let deposit_amount = 1000;
|
|
|
|
let CommonSetup {
|
|
|
|
group_with_tokens,
|
|
|
|
base_token,
|
|
|
|
mut order_placer,
|
|
|
|
mut order_placer2,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
|
|
|
|
|
|
|
// Give account some base token borrows (-500)
|
|
|
|
send_tx(
|
|
|
|
solana,
|
|
|
|
TokenWithdrawInstruction {
|
|
|
|
amount: 1500,
|
|
|
|
allow_borrow: true,
|
|
|
|
account: order_placer.account,
|
|
|
|
owner: order_placer.owner,
|
|
|
|
token_account: context.users[0].token_accounts[1],
|
|
|
|
bank_index: 0,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Cannot buy tokens when deposits are already >0
|
|
|
|
//
|
|
|
|
send_tx(
|
|
|
|
solana,
|
|
|
|
TokenMakeReduceOnly {
|
|
|
|
group: group_with_tokens.group,
|
|
|
|
admin: group_with_tokens.admin,
|
|
|
|
mint: base_token.mint.pubkey,
|
|
|
|
reduce_only: 1,
|
|
|
|
force_close: false,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// cannot place a large order on the book that would deposit too much
|
|
|
|
let err = order_placer.try_bid(1.0, 600, false).await;
|
|
|
|
assert_mango_error(&err, MangoError::TokenInReduceOnlyMode.into(), "".into());
|
|
|
|
|
|
|
|
// a small order is fine
|
|
|
|
order_placer.try_bid(1.0, 100, false).await.unwrap();
|
|
|
|
|
|
|
|
// taking some is fine too
|
|
|
|
order_placer2.ask(1.0, 800).await.unwrap();
|
|
|
|
order_placer.try_bid(1.0, 100, true).await.unwrap();
|
|
|
|
|
|
|
|
// the limit for orders is reduced now, 100 received, 100 on the book
|
|
|
|
let err = order_placer.try_bid(1.0, 400, true).await;
|
|
|
|
assert_mango_error(&err, MangoError::TokenInReduceOnlyMode.into(), "".into());
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-06-15 01:44:11 -07:00
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_place_reducing_when_liquidatable() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(150_000); // Serum3PlaceOrder needs lots
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group, accounts, market etc
|
|
|
|
//
|
|
|
|
let deposit_amount = 1000;
|
|
|
|
let CommonSetup {
|
|
|
|
group_with_tokens,
|
|
|
|
base_token,
|
|
|
|
mut order_placer,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
|
|
|
|
|
|
|
// Give account some base token borrows (-500)
|
|
|
|
send_tx(
|
|
|
|
solana,
|
|
|
|
TokenWithdrawInstruction {
|
|
|
|
amount: 1500,
|
|
|
|
allow_borrow: true,
|
|
|
|
account: order_placer.account,
|
|
|
|
owner: order_placer.owner,
|
|
|
|
token_account: context.users[0].token_accounts[1],
|
|
|
|
bank_index: 0,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Change the base price to make the account liquidatable
|
|
|
|
set_bank_stub_oracle_price(
|
|
|
|
solana,
|
|
|
|
group_with_tokens.group,
|
|
|
|
&base_token,
|
|
|
|
group_with_tokens.admin,
|
|
|
|
10.0,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
assert!(account_init_health(solana, order_placer.account).await < 0.0);
|
|
|
|
|
|
|
|
// can place an order that would close some of the borrows
|
|
|
|
order_placer.try_bid(10.0, 200, false).await.unwrap();
|
|
|
|
|
|
|
|
// if too much base is bought, health would decrease: forbidden
|
|
|
|
let err = order_placer.try_bid(10.0, 800, false).await;
|
|
|
|
assert_mango_error(
|
|
|
|
&err,
|
|
|
|
MangoError::HealthMustBePositiveOrIncrease.into(),
|
|
|
|
"".into(),
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-09-13 00:35:10 -07:00
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_track_bid_ask() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(150_000); // Serum3PlaceOrder needs lots
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group, accounts, market etc
|
|
|
|
//
|
|
|
|
let deposit_amount = 10000;
|
|
|
|
let CommonSetup {
|
|
|
|
serum_market_cookie,
|
|
|
|
mut order_placer,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: highest bid/lowest ask updating
|
|
|
|
//
|
|
|
|
|
2023-12-05 06:39:24 -08:00
|
|
|
let srm = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(srm.highest_placed_bid_inv, 0.0);
|
|
|
|
assert_eq!(srm.lowest_placed_bid_inv, 0.0);
|
|
|
|
assert_eq!(srm.highest_placed_ask, 0.0);
|
|
|
|
assert_eq!(srm.lowest_placed_ask, 0.0);
|
2023-09-13 00:35:10 -07:00
|
|
|
|
|
|
|
order_placer.bid_maker(10.0, 100).await.unwrap();
|
2023-12-05 06:39:24 -08:00
|
|
|
|
|
|
|
let srm = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(srm.highest_placed_bid_inv, 1.0 / 10.0);
|
|
|
|
assert_eq!(srm.lowest_placed_bid_inv, 1.0 / 10.0);
|
|
|
|
assert_eq!(srm.highest_placed_ask, 0.0);
|
|
|
|
assert_eq!(srm.lowest_placed_ask, 0.0);
|
2023-09-13 00:35:10 -07:00
|
|
|
|
|
|
|
order_placer.bid_maker(9.0, 100).await.unwrap();
|
2023-12-05 06:39:24 -08:00
|
|
|
|
|
|
|
let srm = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(srm.highest_placed_bid_inv, 1.0 / 10.0);
|
|
|
|
assert_eq!(srm.lowest_placed_bid_inv, 1.0 / 9.0);
|
|
|
|
assert_eq!(srm.highest_placed_ask, 0.0);
|
|
|
|
assert_eq!(srm.lowest_placed_ask, 0.0);
|
2023-09-13 00:35:10 -07:00
|
|
|
|
|
|
|
order_placer.bid_maker(11.0, 100).await.unwrap();
|
|
|
|
|
2023-12-05 06:39:24 -08:00
|
|
|
let srm = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(srm.highest_placed_bid_inv, 1.0 / 11.0);
|
|
|
|
assert_eq!(srm.lowest_placed_bid_inv, 1.0 / 9.0);
|
|
|
|
assert_eq!(srm.highest_placed_ask, 0.0);
|
|
|
|
assert_eq!(srm.lowest_placed_ask, 0.0);
|
|
|
|
|
2023-09-13 00:35:10 -07:00
|
|
|
order_placer.ask(20.0, 100).await.unwrap();
|
2023-12-05 06:39:24 -08:00
|
|
|
|
|
|
|
let srm = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(srm.highest_placed_bid_inv, 1.0 / 11.0);
|
|
|
|
assert_eq!(srm.lowest_placed_bid_inv, 1.0 / 9.0);
|
|
|
|
assert_eq!(srm.highest_placed_ask, 20.0);
|
|
|
|
assert_eq!(srm.lowest_placed_ask, 20.0);
|
|
|
|
|
2023-09-13 00:35:10 -07:00
|
|
|
order_placer.ask(19.0, 100).await.unwrap();
|
2023-12-05 06:39:24 -08:00
|
|
|
|
|
|
|
let srm = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(srm.highest_placed_bid_inv, 1.0 / 11.0);
|
|
|
|
assert_eq!(srm.lowest_placed_bid_inv, 1.0 / 9.0);
|
|
|
|
assert_eq!(srm.highest_placed_ask, 20.0);
|
|
|
|
assert_eq!(srm.lowest_placed_ask, 19.0);
|
|
|
|
|
2023-09-13 00:35:10 -07:00
|
|
|
order_placer.ask(21.0, 100).await.unwrap();
|
|
|
|
|
2023-12-05 06:39:24 -08:00
|
|
|
let srm = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(srm.highest_placed_bid_inv, 1.0 / 11.0);
|
|
|
|
assert_eq!(srm.lowest_placed_bid_inv, 1.0 / 9.0);
|
|
|
|
assert_eq!(srm.highest_placed_ask, 21.0);
|
|
|
|
assert_eq!(srm.lowest_placed_ask, 19.0);
|
2023-09-13 00:35:10 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: cancellation allows for resets
|
|
|
|
//
|
|
|
|
|
|
|
|
order_placer.cancel_all().await;
|
2023-12-05 06:39:24 -08:00
|
|
|
|
|
|
|
// no immediate change
|
|
|
|
let srm = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(srm.highest_placed_bid_inv, 1.0 / 11.0);
|
|
|
|
assert_eq!(srm.lowest_placed_bid_inv, 1.0 / 9.0);
|
|
|
|
assert_eq!(srm.highest_placed_ask, 21.0);
|
|
|
|
assert_eq!(srm.lowest_placed_ask, 19.0);
|
2023-09-13 00:35:10 -07:00
|
|
|
|
|
|
|
// Process events such that the OutEvent deactivates the closed order on open_orders
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(&serum_market_cookie, &[order_placer.open_orders])
|
|
|
|
.await;
|
|
|
|
|
|
|
|
// takes new value for bid, resets ask
|
|
|
|
order_placer.bid_maker(1.0, 100).await.unwrap();
|
2023-12-05 06:39:24 -08:00
|
|
|
|
|
|
|
let srm = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(srm.highest_placed_bid_inv, 1.0);
|
|
|
|
assert_eq!(srm.lowest_placed_bid_inv, 1.0);
|
|
|
|
assert_eq!(srm.highest_placed_ask, 0.0);
|
|
|
|
assert_eq!(srm.lowest_placed_ask, 0.0);
|
2023-09-13 00:35:10 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: can reset even when there's still an order on the other side
|
|
|
|
//
|
|
|
|
let (oid, _) = order_placer.ask(10.0, 100).await.unwrap();
|
2023-12-05 06:39:24 -08:00
|
|
|
|
|
|
|
let srm = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(srm.highest_placed_bid_inv, 1.0);
|
|
|
|
assert_eq!(srm.lowest_placed_bid_inv, 1.0);
|
|
|
|
assert_eq!(srm.highest_placed_ask, 10.0);
|
|
|
|
assert_eq!(srm.lowest_placed_ask, 10.0);
|
|
|
|
|
2023-09-13 00:35:10 -07:00
|
|
|
order_placer.cancel(oid).await;
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(&serum_market_cookie, &[order_placer.open_orders])
|
|
|
|
.await;
|
|
|
|
order_placer.ask(9.0, 100).await.unwrap();
|
2023-12-05 06:39:24 -08:00
|
|
|
|
|
|
|
let srm = order_placer.mango_serum_orders().await;
|
|
|
|
assert_eq!(srm.highest_placed_bid_inv, 1.0);
|
|
|
|
assert_eq!(srm.lowest_placed_bid_inv, 1.0);
|
|
|
|
assert_eq!(srm.highest_placed_ask, 9.0);
|
|
|
|
assert_eq!(srm.lowest_placed_ask, 9.0);
|
2023-09-13 00:35:10 -07:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-11-02 02:40:31 -07:00
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_track_reserved_deposits() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(150_000); // Serum3PlaceOrder needs lots
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group, accounts, market etc
|
|
|
|
//
|
|
|
|
let deposit_amount = 100000;
|
|
|
|
let CommonSetup {
|
|
|
|
serum_market_cookie,
|
|
|
|
quote_token,
|
|
|
|
base_token,
|
|
|
|
mut order_placer,
|
|
|
|
mut order_placer2,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
let quote_bank = quote_token.bank;
|
|
|
|
let base_bank = base_token.bank;
|
|
|
|
let account = order_placer.account;
|
|
|
|
|
|
|
|
let get_vals = |solana| async move {
|
|
|
|
let account_data = get_mango_account(solana, account).await;
|
|
|
|
let orders = account_data.all_serum3_orders().next().unwrap();
|
|
|
|
let base_bank = solana.get_account::<Bank>(base_bank).await;
|
|
|
|
let quote_bank = solana.get_account::<Bank>(quote_bank).await;
|
|
|
|
(
|
2023-12-05 06:39:24 -08:00
|
|
|
orders.potential_base_tokens,
|
|
|
|
base_bank.potential_serum_tokens,
|
|
|
|
orders.potential_quote_tokens,
|
|
|
|
quote_bank.potential_serum_tokens,
|
2023-11-02 02:40:31 -07:00
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: place a bid and ask and observe tracking
|
|
|
|
//
|
|
|
|
|
|
|
|
order_placer.bid_maker(0.8, 2000).await.unwrap();
|
|
|
|
assert_eq!(get_vals(solana).await, (2000, 2000, 1600, 1600));
|
|
|
|
|
2023-12-05 06:39:24 -08:00
|
|
|
order_placer.ask(1.2, 2000).await.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
get_vals(solana).await,
|
|
|
|
(2 * 2000, 2 * 2000, 1600 + 2400, 1600 + 2400)
|
|
|
|
);
|
|
|
|
|
2023-11-02 02:40:31 -07:00
|
|
|
//
|
|
|
|
// TEST: match partially on both sides, increasing the on-bank reserved amounts
|
|
|
|
// because order_placer2 puts funds into the serum oo
|
|
|
|
//
|
|
|
|
|
|
|
|
order_placer2.bid_taker(1.2, 1000).await.unwrap();
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(
|
|
|
|
&serum_market_cookie,
|
|
|
|
&[order_placer.open_orders, order_placer2.open_orders],
|
|
|
|
)
|
|
|
|
.await;
|
2023-12-05 06:39:24 -08:00
|
|
|
// taker order directly converted to base, no change to quote
|
|
|
|
assert_eq!(get_vals(solana).await, (4000, 4000 + 1000, 4000, 4000));
|
|
|
|
|
|
|
|
// takes out 1000 base
|
2023-11-02 02:40:31 -07:00
|
|
|
order_placer2.settle_v2(false).await;
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(get_vals(solana).await, (4000, 4000, 4000, 4000));
|
2023-11-02 02:40:31 -07:00
|
|
|
|
|
|
|
order_placer2.ask(0.8, 1000).await.unwrap();
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(
|
|
|
|
&serum_market_cookie,
|
|
|
|
&[order_placer.open_orders, order_placer2.open_orders],
|
|
|
|
)
|
|
|
|
.await;
|
2023-12-05 06:39:24 -08:00
|
|
|
// taker order directly converted to quote
|
|
|
|
assert_eq!(get_vals(solana).await, (4000, 4000, 4000, 4000 + 799));
|
|
|
|
|
2023-11-02 02:40:31 -07:00
|
|
|
order_placer2.settle_v2(false).await;
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(get_vals(solana).await, (4000, 4000, 4000, 4000));
|
2023-11-02 02:40:31 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Settlement updates the values
|
|
|
|
//
|
|
|
|
|
|
|
|
order_placer.settle_v2(false).await;
|
2023-12-05 06:39:24 -08:00
|
|
|
// remaining is bid 1000 @ 0.8; ask 1000 @ 1.2
|
|
|
|
assert_eq!(get_vals(solana).await, (2000, 2000, 2000, 2000));
|
2023-11-02 02:40:31 -07:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-11-03 03:20:37 -07:00
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_compute() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(150_000); // Serum3PlaceOrder needs lots
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
let solana = &context.solana;
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group, accounts, market etc
|
|
|
|
//
|
|
|
|
let deposit_amount = 100000;
|
|
|
|
let CommonSetup {
|
|
|
|
serum_market_cookie,
|
|
|
|
mut order_placer,
|
|
|
|
order_placer2,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: check compute per serum match
|
|
|
|
//
|
|
|
|
|
|
|
|
for limit in 1..6 {
|
|
|
|
order_placer.bid_maker(1.0, 100).await.unwrap();
|
|
|
|
order_placer.bid_maker(1.1, 100).await.unwrap();
|
|
|
|
order_placer.bid_maker(1.2, 100).await.unwrap();
|
|
|
|
order_placer.bid_maker(1.3, 100).await.unwrap();
|
|
|
|
order_placer.bid_maker(1.4, 100).await.unwrap();
|
|
|
|
|
|
|
|
let result = send_tx_get_metadata(
|
|
|
|
solana,
|
|
|
|
Serum3PlaceOrderInstruction {
|
|
|
|
side: Serum3Side::Ask,
|
|
|
|
limit_price: (1.0 * 100.0 / 10.0) as u64, // in quote_lot (10) per base lot (100)
|
|
|
|
max_base_qty: 500 / 100, // in base lot (100)
|
|
|
|
max_native_quote_qty_including_fees: (1.0 * (500 as f64)) as u64,
|
|
|
|
self_trade_behavior: Serum3SelfTradeBehavior::AbortTransaction,
|
|
|
|
order_type: Serum3OrderType::Limit,
|
|
|
|
client_order_id: 0,
|
|
|
|
limit,
|
|
|
|
account: order_placer2.account,
|
|
|
|
owner: order_placer2.owner,
|
|
|
|
serum_market: order_placer2.serum_market,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
println!(
|
|
|
|
"CU for serum_place_order matching {limit} orders in sequence: {}",
|
|
|
|
result.metadata.unwrap().compute_units_consumed
|
|
|
|
);
|
|
|
|
|
|
|
|
// many events need processing
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(
|
|
|
|
&serum_market_cookie,
|
|
|
|
&[order_placer.open_orders, order_placer2.open_orders],
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(
|
|
|
|
&serum_market_cookie,
|
|
|
|
&[order_placer.open_orders, order_placer2.open_orders],
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(
|
|
|
|
&serum_market_cookie,
|
|
|
|
&[order_placer.open_orders, order_placer2.open_orders],
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
order_placer.cancel_all().await;
|
|
|
|
order_placer2.cancel_all().await;
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(
|
|
|
|
&serum_market_cookie,
|
|
|
|
&[order_placer.open_orders, order_placer2.open_orders],
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: check compute per serum cancel
|
|
|
|
//
|
|
|
|
|
|
|
|
for limit in 1..6 {
|
|
|
|
for i in 0..limit {
|
|
|
|
order_placer.bid_maker(1.0 + i as f64, 100).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
let result = send_tx_get_metadata(
|
|
|
|
solana,
|
|
|
|
Serum3CancelAllOrdersInstruction {
|
|
|
|
account: order_placer.account,
|
|
|
|
owner: order_placer.owner,
|
|
|
|
serum_market: order_placer.serum_market,
|
|
|
|
limit: 10,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
println!(
|
|
|
|
"CU for serum_cancel_all_order for {limit} orders: {}",
|
|
|
|
result.metadata.unwrap().compute_units_consumed
|
|
|
|
);
|
|
|
|
|
|
|
|
context
|
|
|
|
.serum
|
|
|
|
.consume_spot_events(
|
|
|
|
&serum_market_cookie,
|
|
|
|
&[order_placer.open_orders, order_placer2.open_orders],
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-11-22 14:40:48 -08:00
|
|
|
#[tokio::test]
|
|
|
|
async fn test_fallback_oracle_serum() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(150_000);
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
|
|
|
let fallback_oracle_kp = TestKeypair::new();
|
|
|
|
let fallback_oracle = fallback_oracle_kp.pubkey();
|
|
|
|
let owner = context.users[0].key;
|
|
|
|
let payer = context.users[1].key;
|
|
|
|
let payer_token_accounts = &context.users[1].token_accounts[0..3];
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group and an account
|
|
|
|
//
|
2023-11-28 20:55:19 -08:00
|
|
|
let deposit_amount = 1_000;
|
|
|
|
let CommonSetup {
|
|
|
|
group_with_tokens,
|
|
|
|
quote_token,
|
|
|
|
base_token,
|
|
|
|
mut order_placer,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
|
|
|
let GroupWithTokens {
|
|
|
|
group,
|
2023-11-22 14:40:48 -08:00
|
|
|
admin,
|
2023-11-28 20:55:19 -08:00
|
|
|
tokens,
|
|
|
|
..
|
|
|
|
} = group_with_tokens;
|
2023-11-22 14:40:48 -08:00
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a fallback oracle
|
|
|
|
//
|
|
|
|
send_tx(
|
|
|
|
solana,
|
|
|
|
StubOracleCreate {
|
|
|
|
oracle: fallback_oracle_kp,
|
|
|
|
group,
|
2023-11-28 20:55:19 -08:00
|
|
|
mint: tokens[2].mint.pubkey,
|
2023-11-22 14:40:48 -08:00
|
|
|
admin,
|
|
|
|
payer,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Add a fallback oracle
|
|
|
|
//
|
|
|
|
send_tx(
|
|
|
|
solana,
|
|
|
|
TokenEdit {
|
|
|
|
group,
|
|
|
|
admin,
|
2023-11-28 20:55:19 -08:00
|
|
|
mint: tokens[2].mint.pubkey,
|
2023-11-22 14:40:48 -08:00
|
|
|
fallback_oracle,
|
|
|
|
options: mango_v4::instruction::TokenEdit {
|
|
|
|
set_fallback_oracle: true,
|
|
|
|
..token_edit_instruction_default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let bank_data: Bank = solana.get_account(tokens[2].bank).await;
|
|
|
|
assert!(bank_data.fallback_oracle == fallback_oracle);
|
|
|
|
|
|
|
|
// Create some token1 borrows
|
|
|
|
send_tx(
|
|
|
|
solana,
|
|
|
|
TokenWithdrawInstruction {
|
2023-11-28 20:55:19 -08:00
|
|
|
amount: 1_500,
|
2023-11-22 14:40:48 -08:00
|
|
|
allow_borrow: true,
|
2023-11-28 20:55:19 -08:00
|
|
|
account: order_placer.account,
|
2023-11-22 14:40:48 -08:00
|
|
|
owner,
|
|
|
|
token_account: payer_token_accounts[2],
|
|
|
|
bank_index: 0,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Make oracle invalid by increasing deviation
|
|
|
|
send_tx(
|
|
|
|
solana,
|
|
|
|
StubOracleSetTestInstruction {
|
|
|
|
oracle: tokens[2].oracle,
|
|
|
|
group,
|
2023-11-28 20:55:19 -08:00
|
|
|
mint: tokens[2].mint.pubkey,
|
2023-11-22 14:40:48 -08:00
|
|
|
admin,
|
|
|
|
price: 1.0,
|
|
|
|
last_update_slot: 0,
|
|
|
|
deviation: 100.0,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Place a failing order
|
|
|
|
//
|
|
|
|
let limit_price = 1.0;
|
|
|
|
let max_base = 100;
|
|
|
|
let order_fut = order_placer.try_bid(limit_price, max_base, false).await;
|
|
|
|
assert_mango_error(
|
|
|
|
&order_fut,
|
|
|
|
6023,
|
|
|
|
"an oracle does not reach the confidence threshold".to_string(),
|
|
|
|
);
|
|
|
|
|
|
|
|
// now send txn with a fallback oracle in the remaining accounts
|
|
|
|
let fallback_oracle_meta = AccountMeta {
|
|
|
|
pubkey: fallback_oracle,
|
|
|
|
is_writable: false,
|
|
|
|
is_signer: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
let client_order_id = order_placer.inc_client_order_id();
|
|
|
|
let place_ix = Serum3PlaceOrderInstruction {
|
|
|
|
side: Serum3Side::Bid,
|
|
|
|
limit_price: (limit_price * 100.0 / 10.0) as u64, // in quote_lot (10) per base lot (100)
|
|
|
|
max_base_qty: max_base / 100, // in base lot (100)
|
|
|
|
// 4 bps taker fees added in
|
|
|
|
max_native_quote_qty_including_fees: (limit_price * (max_base as f64) * (1.0)).ceil()
|
|
|
|
as u64,
|
|
|
|
self_trade_behavior: Serum3SelfTradeBehavior::AbortTransaction,
|
|
|
|
order_type: Serum3OrderType::Limit,
|
|
|
|
client_order_id,
|
|
|
|
limit: 10,
|
|
|
|
account: order_placer.account,
|
|
|
|
owner: order_placer.owner,
|
|
|
|
serum_market: order_placer.serum_market,
|
|
|
|
};
|
|
|
|
|
|
|
|
let result = send_tx_with_extra_accounts(solana, place_ix, vec![fallback_oracle_meta])
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
result.result.unwrap();
|
|
|
|
|
2023-11-28 20:55:19 -08:00
|
|
|
let account_data = get_mango_account(solana, order_placer.account).await;
|
2023-11-22 14:40:48 -08:00
|
|
|
assert_eq!(
|
|
|
|
account_data
|
|
|
|
.token_position_by_raw_index(0)
|
|
|
|
.unwrap()
|
|
|
|
.in_use_count,
|
|
|
|
1
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
account_data
|
|
|
|
.token_position_by_raw_index(1)
|
|
|
|
.unwrap()
|
|
|
|
.in_use_count,
|
2023-11-28 20:55:19 -08:00
|
|
|
1
|
2023-11-22 14:40:48 -08:00
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
account_data
|
|
|
|
.token_position_by_raw_index(2)
|
|
|
|
.unwrap()
|
|
|
|
.in_use_count,
|
2023-11-28 20:55:19 -08:00
|
|
|
0
|
2023-11-22 14:40:48 -08:00
|
|
|
);
|
|
|
|
let serum_orders = account_data.serum3_orders_by_raw_index(0).unwrap();
|
|
|
|
assert_eq!(serum_orders.base_borrows_without_fee, 0);
|
|
|
|
assert_eq!(serum_orders.quote_borrows_without_fee, 0);
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(serum_orders.potential_base_tokens, 100);
|
|
|
|
assert_eq!(serum_orders.potential_quote_tokens, 100);
|
2023-11-22 14:40:48 -08:00
|
|
|
|
|
|
|
let base_bank = solana.get_account::<Bank>(base_token.bank).await;
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(base_bank.potential_serum_tokens, 100);
|
2023-11-22 14:40:48 -08:00
|
|
|
let quote_bank = solana.get_account::<Bank>(quote_token.bank).await;
|
2023-12-05 06:39:24 -08:00
|
|
|
assert_eq!(quote_bank.potential_serum_tokens, 100);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_serum_bands() -> Result<(), TransportError> {
|
|
|
|
let mut test_builder = TestContextBuilder::new();
|
|
|
|
test_builder.test().set_compute_max_units(150_000); // Serum3PlaceOrder needs lots
|
|
|
|
let context = test_builder.start_default().await;
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create a group, accounts, market etc
|
|
|
|
//
|
|
|
|
let deposit_amount = 10000;
|
|
|
|
let CommonSetup {
|
|
|
|
group_with_tokens,
|
|
|
|
mut order_placer,
|
|
|
|
quote_token,
|
|
|
|
base_token,
|
|
|
|
..
|
|
|
|
} = common_setup(&context, deposit_amount).await;
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Set oracle price for market to 100
|
|
|
|
//
|
|
|
|
set_bank_stub_oracle_price(
|
|
|
|
solana,
|
|
|
|
group_with_tokens.group,
|
|
|
|
&base_token,
|
|
|
|
group_with_tokens.admin,
|
|
|
|
200.0,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
set_bank_stub_oracle_price(
|
|
|
|
solana,
|
|
|
|
group_with_tokens.group,
|
|
|
|
"e_token,
|
|
|
|
group_with_tokens.admin,
|
|
|
|
2.0,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: can place way over/under oracle
|
|
|
|
//
|
|
|
|
|
|
|
|
order_placer.bid_maker(1.0, 100).await.unwrap();
|
|
|
|
order_placer.ask(200.0, 100).await.unwrap();
|
|
|
|
order_placer.cancel_all().await;
|
|
|
|
|
|
|
|
//
|
|
|
|
// TEST: Can't when bands are enabled
|
|
|
|
//
|
|
|
|
send_tx(
|
|
|
|
solana,
|
|
|
|
Serum3EditMarketInstruction {
|
|
|
|
group: group_with_tokens.group,
|
|
|
|
admin: group_with_tokens.admin,
|
|
|
|
market: order_placer.serum_market,
|
|
|
|
options: mango_v4::instruction::Serum3EditMarket {
|
|
|
|
oracle_price_band_opt: Some(0.5),
|
|
|
|
..serum3_edit_market_instruction_default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let r = order_placer.try_bid(65.0, 100, false).await;
|
|
|
|
assert!(r.is_err());
|
|
|
|
let r = order_placer.try_ask(151.0, 100).await;
|
|
|
|
assert!(r.is_err());
|
|
|
|
|
|
|
|
order_placer.try_bid(67.0, 100, false).await.unwrap();
|
|
|
|
order_placer.try_ask(149.0, 100).await.unwrap();
|
|
|
|
|
2023-11-22 14:40:48 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-03-03 05:04:45 -08:00
|
|
|
struct CommonSetup {
|
2023-03-20 03:18:11 -07:00
|
|
|
group_with_tokens: GroupWithTokens,
|
2023-03-03 05:04:45 -08:00
|
|
|
serum_market_cookie: SpotMarketCookie,
|
2023-03-20 03:18:11 -07:00
|
|
|
quote_token: crate::program_test::mango_setup::Token,
|
|
|
|
base_token: crate::program_test::mango_setup::Token,
|
2023-03-03 05:04:45 -08:00
|
|
|
order_placer: SerumOrderPlacer,
|
|
|
|
order_placer2: SerumOrderPlacer,
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn common_setup(context: &TestContext, deposit_amount: u64) -> CommonSetup {
|
|
|
|
let admin = TestKeypair::new();
|
|
|
|
let owner = context.users[0].key;
|
|
|
|
let payer = context.users[1].key;
|
|
|
|
let mints = &context.mints[0..3];
|
|
|
|
|
|
|
|
let solana = &context.solana.clone();
|
|
|
|
|
|
|
|
let group_with_tokens = GroupWithTokensConfig {
|
|
|
|
admin,
|
|
|
|
payer,
|
|
|
|
mints: mints.to_vec(),
|
|
|
|
..GroupWithTokensConfig::default()
|
|
|
|
}
|
|
|
|
.create(solana)
|
|
|
|
.await;
|
|
|
|
let group = group_with_tokens.group;
|
|
|
|
let tokens = group_with_tokens.tokens.clone();
|
|
|
|
let base_token = &tokens[1];
|
|
|
|
let quote_token = &tokens[0];
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create serum market
|
|
|
|
//
|
|
|
|
let serum_market_cookie = context
|
|
|
|
.serum
|
|
|
|
.list_spot_market(&base_token.mint, "e_token.mint)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Register a serum market
|
|
|
|
//
|
|
|
|
let serum_market = send_tx(
|
|
|
|
solana,
|
|
|
|
Serum3RegisterMarketInstruction {
|
|
|
|
group,
|
|
|
|
admin,
|
|
|
|
serum_program: context.serum.program_id,
|
|
|
|
serum_market_external: serum_market_cookie.market,
|
|
|
|
market_index: 0,
|
|
|
|
base_bank: base_token.bank,
|
|
|
|
quote_bank: quote_token.bank,
|
|
|
|
payer,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.serum_market;
|
|
|
|
|
|
|
|
//
|
|
|
|
// SETUP: Create accounts
|
|
|
|
//
|
|
|
|
let account = create_funded_account(
|
|
|
|
&solana,
|
|
|
|
group,
|
|
|
|
owner,
|
|
|
|
0,
|
|
|
|
&context.users[1],
|
|
|
|
mints,
|
|
|
|
deposit_amount,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
let account2 = create_funded_account(
|
|
|
|
&solana,
|
|
|
|
group,
|
|
|
|
owner,
|
|
|
|
2,
|
|
|
|
&context.users[1],
|
|
|
|
mints,
|
|
|
|
deposit_amount,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
// to have enough funds in the vaults
|
|
|
|
create_funded_account(
|
|
|
|
&solana,
|
|
|
|
group,
|
|
|
|
owner,
|
|
|
|
3,
|
|
|
|
&context.users[1],
|
|
|
|
mints,
|
|
|
|
10000000,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let open_orders = send_tx(
|
|
|
|
solana,
|
|
|
|
Serum3CreateOpenOrdersInstruction {
|
|
|
|
account,
|
|
|
|
serum_market,
|
|
|
|
owner,
|
|
|
|
payer,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.open_orders;
|
|
|
|
|
|
|
|
let open_orders2 = send_tx(
|
|
|
|
solana,
|
|
|
|
Serum3CreateOpenOrdersInstruction {
|
|
|
|
account: account2,
|
|
|
|
serum_market,
|
|
|
|
owner,
|
|
|
|
payer,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.open_orders;
|
|
|
|
|
|
|
|
let order_placer = SerumOrderPlacer {
|
|
|
|
solana: solana.clone(),
|
|
|
|
serum: context.serum.clone(),
|
|
|
|
account,
|
|
|
|
owner: owner.clone(),
|
|
|
|
serum_market,
|
|
|
|
open_orders,
|
|
|
|
next_client_order_id: 0,
|
|
|
|
};
|
|
|
|
let order_placer2 = SerumOrderPlacer {
|
|
|
|
solana: solana.clone(),
|
|
|
|
serum: context.serum.clone(),
|
|
|
|
account: account2,
|
|
|
|
owner: owner.clone(),
|
|
|
|
serum_market,
|
|
|
|
open_orders: open_orders2,
|
|
|
|
next_client_order_id: 100000,
|
|
|
|
};
|
|
|
|
|
|
|
|
CommonSetup {
|
2023-03-20 03:18:11 -07:00
|
|
|
group_with_tokens,
|
2023-03-03 05:04:45 -08:00
|
|
|
serum_market_cookie,
|
2023-03-20 03:18:11 -07:00
|
|
|
quote_token: quote_token.clone(),
|
|
|
|
base_token: base_token.clone(),
|
2023-03-03 05:04:45 -08:00
|
|
|
order_placer,
|
|
|
|
order_placer2,
|
|
|
|
}
|
|
|
|
}
|