Rust client: auto close spot account if needed to place a new order (#877)
rust client: - add serum3 place order to command - add serum3 create open orders command - add serum3 close open orders command - auto create serum3 open orders if needed when placing a new order - auto close serum3 slot if needed when placing a new order & also close unused token if needed to place a serum3 order
This commit is contained in:
parent
34d86ef0f4
commit
5d29eb2f0b
|
@ -1,5 +1,6 @@
|
||||||
use clap::clap_derive::ArgEnum;
|
use clap::clap_derive::ArgEnum;
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
use mango_v4::accounts_ix::{Serum3OrderType, Serum3SelfTradeBehavior, Serum3Side};
|
||||||
use mango_v4::state::{PlaceOrderType, SelfTradeBehavior, Side};
|
use mango_v4::state::{PlaceOrderType, SelfTradeBehavior, Side};
|
||||||
use mango_v4_client::{
|
use mango_v4_client::{
|
||||||
keypair_from_cli, pubkey_from_cli, Client, MangoClient, TransactionBuilderConfig,
|
keypair_from_cli, pubkey_from_cli, Client, MangoClient, TransactionBuilderConfig,
|
||||||
|
@ -126,6 +127,63 @@ struct PerpPlaceOrder {
|
||||||
rpc: Rpc,
|
rpc: Rpc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug, Clone)]
|
||||||
|
struct Serum3CreateOpenOrders {
|
||||||
|
#[clap(long)]
|
||||||
|
account: String,
|
||||||
|
|
||||||
|
/// also pays for everything
|
||||||
|
#[clap(short, long)]
|
||||||
|
owner: String,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
market_name: String,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
rpc: Rpc,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug, Clone)]
|
||||||
|
struct Serum3CloseOpenOrders {
|
||||||
|
#[clap(long)]
|
||||||
|
account: String,
|
||||||
|
|
||||||
|
/// also pays for everything
|
||||||
|
#[clap(short, long)]
|
||||||
|
owner: String,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
market_name: String,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
rpc: Rpc,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug, Clone)]
|
||||||
|
struct Serum3PlaceOrder {
|
||||||
|
#[clap(long)]
|
||||||
|
account: String,
|
||||||
|
|
||||||
|
/// also pays for everything
|
||||||
|
#[clap(short, long)]
|
||||||
|
owner: String,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
market_name: String,
|
||||||
|
|
||||||
|
#[clap(long, value_enum)]
|
||||||
|
side: CliSide,
|
||||||
|
|
||||||
|
#[clap(short, long)]
|
||||||
|
price: f64,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
quantity: f64,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
rpc: Rpc,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug, Clone)]
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
enum Command {
|
enum Command {
|
||||||
CreateAccount(CreateAccount),
|
CreateAccount(CreateAccount),
|
||||||
|
@ -167,6 +225,9 @@ enum Command {
|
||||||
output: String,
|
output: String,
|
||||||
},
|
},
|
||||||
PerpPlaceOrder(PerpPlaceOrder),
|
PerpPlaceOrder(PerpPlaceOrder),
|
||||||
|
Serum3CloseOpenOrders(Serum3CloseOpenOrders),
|
||||||
|
Serum3CreateOpenOrders(Serum3CreateOpenOrders),
|
||||||
|
Serum3PlaceOrder(Serum3PlaceOrder),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rpc {
|
impl Rpc {
|
||||||
|
@ -326,6 +387,65 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||||
.await?;
|
.await?;
|
||||||
println!("{}", txsig);
|
println!("{}", txsig);
|
||||||
}
|
}
|
||||||
|
Command::Serum3CreateOpenOrders(cmd) => {
|
||||||
|
let client = cmd.rpc.client(Some(&cmd.owner))?;
|
||||||
|
let account = pubkey_from_cli(&cmd.account);
|
||||||
|
let owner = Arc::new(keypair_from_cli(&cmd.owner));
|
||||||
|
let client = MangoClient::new_for_existing_account(client, account, owner).await?;
|
||||||
|
|
||||||
|
let txsig = client.serum3_create_open_orders(&cmd.market_name).await?;
|
||||||
|
println!("{}", txsig);
|
||||||
|
}
|
||||||
|
Command::Serum3CloseOpenOrders(cmd) => {
|
||||||
|
let client = cmd.rpc.client(Some(&cmd.owner))?;
|
||||||
|
let account = pubkey_from_cli(&cmd.account);
|
||||||
|
let owner = Arc::new(keypair_from_cli(&cmd.owner));
|
||||||
|
let client = MangoClient::new_for_existing_account(client, account, owner).await?;
|
||||||
|
|
||||||
|
let txsig = client.serum3_close_open_orders(&cmd.market_name).await?;
|
||||||
|
println!("{}", txsig);
|
||||||
|
}
|
||||||
|
Command::Serum3PlaceOrder(cmd) => {
|
||||||
|
let client = cmd.rpc.client(Some(&cmd.owner))?;
|
||||||
|
let account = pubkey_from_cli(&cmd.account);
|
||||||
|
let owner = Arc::new(keypair_from_cli(&cmd.owner));
|
||||||
|
let client = MangoClient::new_for_existing_account(client, account, owner).await?;
|
||||||
|
let market_index = client.context.serum3_market_index(&cmd.market_name);
|
||||||
|
let market = client.context.serum3(market_index);
|
||||||
|
let base_token = client.context.token(market.base_token_index);
|
||||||
|
let quote_token = client.context.token(market.quote_token_index);
|
||||||
|
|
||||||
|
fn native(x: f64, b: u32) -> u64 {
|
||||||
|
(x * (10_i64.pow(b)) as f64) as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
// coin_lot_size = base lot size ?
|
||||||
|
// cf priceNumberToLots
|
||||||
|
let price_lots = native(cmd.price, quote_token.decimals as u32) * market.coin_lot_size
|
||||||
|
/ (native(1.0, base_token.decimals as u32) * market.pc_lot_size);
|
||||||
|
|
||||||
|
// cf baseSizeNumberToLots
|
||||||
|
let max_base_lots =
|
||||||
|
native(cmd.quantity, base_token.decimals as u32) / market.coin_lot_size;
|
||||||
|
|
||||||
|
let txsig = client
|
||||||
|
.serum3_place_order(
|
||||||
|
&cmd.market_name,
|
||||||
|
match cmd.side {
|
||||||
|
CliSide::Bid => Serum3Side::Bid,
|
||||||
|
CliSide::Ask => Serum3Side::Ask,
|
||||||
|
},
|
||||||
|
price_lots,
|
||||||
|
max_base_lots as u64,
|
||||||
|
((price_lots * max_base_lots) as f64 * 1.01) as u64,
|
||||||
|
Serum3SelfTradeBehavior::AbortTransaction,
|
||||||
|
Serum3OrderType::Limit,
|
||||||
|
SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(),
|
||||||
|
10,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
println!("{}", txsig);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anchor_client::ClientError::AnchorError;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -126,6 +127,7 @@ impl ClientBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
config: ClientConfig,
|
config: ClientConfig,
|
||||||
rpc_async: RpcClientAsync,
|
rpc_async: RpcClientAsync,
|
||||||
|
@ -658,6 +660,45 @@ impl MangoClient {
|
||||||
// Serum3
|
// Serum3
|
||||||
//
|
//
|
||||||
|
|
||||||
|
pub fn serum3_close_open_orders_instruction(
|
||||||
|
&self,
|
||||||
|
market_index: Serum3MarketIndex,
|
||||||
|
) -> PreparedInstructions {
|
||||||
|
let account_pubkey = self.mango_account_address;
|
||||||
|
let s3 = self.context.serum3(market_index);
|
||||||
|
|
||||||
|
let open_orders = self.serum3_create_open_orders_address(market_index);
|
||||||
|
|
||||||
|
PreparedInstructions::from_single(
|
||||||
|
Instruction {
|
||||||
|
program_id: mango_v4::id(),
|
||||||
|
accounts: anchor_lang::ToAccountMetas::to_account_metas(
|
||||||
|
&mango_v4::accounts::Serum3CloseOpenOrders {
|
||||||
|
group: self.group(),
|
||||||
|
account: account_pubkey,
|
||||||
|
serum_market: s3.address,
|
||||||
|
serum_program: s3.serum_program,
|
||||||
|
serum_market_external: s3.serum_market_external,
|
||||||
|
open_orders,
|
||||||
|
owner: self.owner(),
|
||||||
|
sol_destination: self.owner(),
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
data: anchor_lang::InstructionData::data(
|
||||||
|
&mango_v4::instruction::Serum3CloseOpenOrders {},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
self.context.compute_estimates.cu_per_mango_instruction,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn serum3_close_open_orders(&self, name: &str) -> anyhow::Result<Signature> {
|
||||||
|
let market_index = self.context.serum3_market_index(name);
|
||||||
|
let ix = self.serum3_close_open_orders_instruction(market_index);
|
||||||
|
self.send_and_confirm_owner_tx(ix.to_instructions()).await
|
||||||
|
}
|
||||||
|
|
||||||
pub fn serum3_create_open_orders_instruction(
|
pub fn serum3_create_open_orders_instruction(
|
||||||
&self,
|
&self,
|
||||||
market_index: Serum3MarketIndex,
|
market_index: Serum3MarketIndex,
|
||||||
|
@ -665,15 +706,7 @@ impl MangoClient {
|
||||||
let account_pubkey = self.mango_account_address;
|
let account_pubkey = self.mango_account_address;
|
||||||
let s3 = self.context.serum3(market_index);
|
let s3 = self.context.serum3(market_index);
|
||||||
|
|
||||||
let open_orders = Pubkey::find_program_address(
|
let open_orders = self.serum3_create_open_orders_address(market_index);
|
||||||
&[
|
|
||||||
b"Serum3OO".as_ref(),
|
|
||||||
account_pubkey.as_ref(),
|
|
||||||
s3.address.as_ref(),
|
|
||||||
],
|
|
||||||
&mango_v4::ID,
|
|
||||||
)
|
|
||||||
.0;
|
|
||||||
|
|
||||||
Instruction {
|
Instruction {
|
||||||
program_id: mango_v4::id(),
|
program_id: mango_v4::id(),
|
||||||
|
@ -698,6 +731,23 @@ impl MangoClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serum3_create_open_orders_address(&self, market_index: Serum3MarketIndex) -> Pubkey {
|
||||||
|
let account_pubkey = self.mango_account_address;
|
||||||
|
let s3 = self.context.serum3(market_index);
|
||||||
|
|
||||||
|
let open_orders = Pubkey::find_program_address(
|
||||||
|
&[
|
||||||
|
b"Serum3OO".as_ref(),
|
||||||
|
account_pubkey.as_ref(),
|
||||||
|
s3.address.as_ref(),
|
||||||
|
],
|
||||||
|
&mango_v4::ID,
|
||||||
|
)
|
||||||
|
.0;
|
||||||
|
|
||||||
|
open_orders
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn serum3_create_open_orders(&self, name: &str) -> anyhow::Result<Signature> {
|
pub async fn serum3_create_open_orders(&self, name: &str) -> anyhow::Result<Signature> {
|
||||||
let market_index = self.context.serum3_market_index(name);
|
let market_index = self.context.serum3_market_index(name);
|
||||||
let ix = self.serum3_create_open_orders_instruction(market_index);
|
let ix = self.serum3_create_open_orders_instruction(market_index);
|
||||||
|
@ -721,20 +771,22 @@ impl MangoClient {
|
||||||
let s3 = self.context.serum3(market_index);
|
let s3 = self.context.serum3(market_index);
|
||||||
let base = self.context.serum3_base_token(market_index);
|
let base = self.context.serum3_base_token(market_index);
|
||||||
let quote = self.context.serum3_quote_token(market_index);
|
let quote = self.context.serum3_quote_token(market_index);
|
||||||
let open_orders = account
|
let (payer_token, receiver_token) = match side {
|
||||||
.serum3_orders(market_index)
|
Serum3Side::Bid => ("e, &base),
|
||||||
.expect("oo is created")
|
Serum3Side::Ask => (&base, "e),
|
||||||
.open_orders;
|
};
|
||||||
|
|
||||||
|
let open_orders = account.serum3_orders(market_index).map(|x| x.open_orders)?;
|
||||||
|
|
||||||
let (health_check_metas, health_cu) = self
|
let (health_check_metas, health_cu) = self
|
||||||
.derive_health_check_remaining_account_metas(account, vec![], vec![], vec![])
|
.derive_health_check_remaining_account_metas(
|
||||||
|
&account,
|
||||||
|
vec![],
|
||||||
|
vec![receiver_token.token_index],
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let payer_token = match side {
|
|
||||||
Serum3Side::Bid => "e,
|
|
||||||
Serum3Side::Ask => &base,
|
|
||||||
};
|
|
||||||
|
|
||||||
let ixs = PreparedInstructions::from_single(
|
let ixs = PreparedInstructions::from_single(
|
||||||
Instruction {
|
Instruction {
|
||||||
program_id: mango_v4::id(),
|
program_id: mango_v4::id(),
|
||||||
|
@ -766,7 +818,7 @@ impl MangoClient {
|
||||||
ams
|
ams
|
||||||
},
|
},
|
||||||
data: anchor_lang::InstructionData::data(
|
data: anchor_lang::InstructionData::data(
|
||||||
&mango_v4::instruction::Serum3PlaceOrder {
|
&mango_v4::instruction::Serum3PlaceOrderV2 {
|
||||||
side,
|
side,
|
||||||
limit_price,
|
limit_price,
|
||||||
max_base_qty,
|
max_base_qty,
|
||||||
|
@ -785,6 +837,205 @@ impl MangoClient {
|
||||||
Ok(ixs)
|
Ok(ixs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub async fn serum3_create_or_replace_account_instruction(
|
||||||
|
&self,
|
||||||
|
mut account: &mut MangoAccountValue,
|
||||||
|
market_index: Serum3MarketIndex,
|
||||||
|
side: Serum3Side,
|
||||||
|
) -> anyhow::Result<PreparedInstructions> {
|
||||||
|
let mut ixs = PreparedInstructions::new();
|
||||||
|
|
||||||
|
let base = self.context.serum3_base_token(market_index);
|
||||||
|
let quote = self.context.serum3_quote_token(market_index);
|
||||||
|
let (payer_token, receiver_token) = match side {
|
||||||
|
Serum3Side::Bid => ("e, &base),
|
||||||
|
Serum3Side::Ask => (&base, "e),
|
||||||
|
};
|
||||||
|
|
||||||
|
let open_orders_opt = account
|
||||||
|
.serum3_orders(market_index)
|
||||||
|
.map(|x| x.open_orders)
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
let mut missing_tokens = false;
|
||||||
|
|
||||||
|
let token_replace_ixs = self
|
||||||
|
.find_existing_or_try_to_replace_token_positions(
|
||||||
|
&mut account,
|
||||||
|
&[payer_token.token_index, receiver_token.token_index],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
match token_replace_ixs {
|
||||||
|
Ok(res) => {
|
||||||
|
ixs.append(res);
|
||||||
|
}
|
||||||
|
Err(_) => missing_tokens = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if open_orders_opt.is_none() {
|
||||||
|
let has_available_slot = account.all_serum3_orders().any(|p| !p.is_active());
|
||||||
|
let should_close_one_open_orders_account = !has_available_slot || missing_tokens;
|
||||||
|
|
||||||
|
if should_close_one_open_orders_account {
|
||||||
|
ixs.append(
|
||||||
|
self.deactivate_first_active_unused_serum3_orders(&mut account)
|
||||||
|
.await?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case of missing token slots
|
||||||
|
// try again to create, as maybe deactivating the market slot resulted in some token being now unused
|
||||||
|
// but this time, in case of error, propagate to caller
|
||||||
|
if missing_tokens {
|
||||||
|
ixs.append(
|
||||||
|
self.find_existing_or_try_to_replace_token_positions(
|
||||||
|
&mut account,
|
||||||
|
&[payer_token.token_index, receiver_token.token_index],
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ixs.push(
|
||||||
|
self.serum3_create_open_orders_instruction(market_index),
|
||||||
|
self.context.compute_estimates.cu_per_mango_instruction,
|
||||||
|
);
|
||||||
|
|
||||||
|
let created_open_orders = self.serum3_create_open_orders_address(market_index);
|
||||||
|
|
||||||
|
account.create_serum3_orders(market_index)?.open_orders = created_open_orders;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ixs)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn deactivate_first_active_unused_serum3_orders(
|
||||||
|
&self,
|
||||||
|
account: &mut MangoAccountValue,
|
||||||
|
) -> anyhow::Result<PreparedInstructions> {
|
||||||
|
let mut serum3_closable_order_market_index = None;
|
||||||
|
|
||||||
|
for p in account.all_serum3_orders() {
|
||||||
|
let open_orders_acc = self
|
||||||
|
.account_fetcher
|
||||||
|
.fetch_raw_account(&p.open_orders)
|
||||||
|
.await?;
|
||||||
|
let open_orders_bytes = open_orders_acc.data();
|
||||||
|
let open_orders_data: &serum_dex::state::OpenOrders = bytemuck::from_bytes(
|
||||||
|
&open_orders_bytes[5..5 + std::mem::size_of::<serum_dex::state::OpenOrders>()],
|
||||||
|
);
|
||||||
|
|
||||||
|
let is_closable = open_orders_data.free_slot_bits == u128::MAX
|
||||||
|
&& open_orders_data.native_coin_total == 0
|
||||||
|
&& open_orders_data.native_pc_total == 0;
|
||||||
|
|
||||||
|
if is_closable {
|
||||||
|
serum3_closable_order_market_index = Some(p.market_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let first_closable_slot =
|
||||||
|
serum3_closable_order_market_index.expect("couldn't find any serum3 slot available");
|
||||||
|
|
||||||
|
let ixs = self.serum3_close_open_orders_instruction(first_closable_slot);
|
||||||
|
|
||||||
|
let first_closable_market = account.serum3_orders(first_closable_slot)?;
|
||||||
|
let (tk1, tk2) = (
|
||||||
|
first_closable_market.base_token_index,
|
||||||
|
first_closable_market.quote_token_index,
|
||||||
|
);
|
||||||
|
account.token_position_mut(tk1)?.0.decrement_in_use();
|
||||||
|
account.token_position_mut(tk2)?.0.decrement_in_use();
|
||||||
|
account.deactivate_serum3_orders(first_closable_slot)?;
|
||||||
|
|
||||||
|
Ok(ixs)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn find_existing_or_try_to_replace_token_positions(
|
||||||
|
&self,
|
||||||
|
account: &mut MangoAccountValue,
|
||||||
|
token_indexes: &[TokenIndex],
|
||||||
|
) -> anyhow::Result<PreparedInstructions> {
|
||||||
|
let mut ixs = PreparedInstructions::new();
|
||||||
|
|
||||||
|
for token_index in token_indexes {
|
||||||
|
let result = self
|
||||||
|
.find_existing_or_try_to_replace_token_position(account, *token_index)
|
||||||
|
.await?;
|
||||||
|
if let Some(ix) = result {
|
||||||
|
ixs.append(ix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ixs)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn find_existing_or_try_to_replace_token_position(
|
||||||
|
&self,
|
||||||
|
account: &mut MangoAccountValue,
|
||||||
|
token_index: TokenIndex,
|
||||||
|
) -> anyhow::Result<Option<PreparedInstructions>> {
|
||||||
|
let token_position_missing = account
|
||||||
|
.ensure_token_position(token_index)
|
||||||
|
.is_anchor_error_with_code(MangoError::NoFreeTokenPositionIndex.error_code());
|
||||||
|
|
||||||
|
if !token_position_missing {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ixs = self.deactivate_first_active_unused_token(account).await?;
|
||||||
|
account.ensure_token_position(token_index)?;
|
||||||
|
|
||||||
|
Ok(Some(ixs))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn deactivate_first_active_unused_token(
|
||||||
|
&self,
|
||||||
|
account: &mut MangoAccountValue,
|
||||||
|
) -> anyhow::Result<PreparedInstructions> {
|
||||||
|
let closable_tokens = account
|
||||||
|
.all_token_positions()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, p)| p.is_active() && !p.is_in_use());
|
||||||
|
|
||||||
|
let mut closable_token_position_raw_index_opt = None;
|
||||||
|
let mut closable_token_bank_opt = None;
|
||||||
|
|
||||||
|
for (closable_token_position_raw_index, closable_token_position) in closable_tokens {
|
||||||
|
let bank = self.first_bank(closable_token_position.token_index).await?;
|
||||||
|
let native_balance = closable_token_position.native(&bank);
|
||||||
|
|
||||||
|
if native_balance < I80F48::ZERO {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if native_balance > I80F48::ONE {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
closable_token_position_raw_index_opt = Some(closable_token_position_raw_index);
|
||||||
|
closable_token_bank_opt = Some(bank);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if closable_token_bank_opt.is_none() {
|
||||||
|
return Err(AnchorError(MangoError::NoFreeTokenPositionIndex.into()).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let withdraw_ixs = self
|
||||||
|
.token_withdraw_instructions(
|
||||||
|
&account,
|
||||||
|
closable_token_bank_opt.unwrap().mint,
|
||||||
|
u64::MAX,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
account.deactivate_token_position(closable_token_position_raw_index_opt.unwrap());
|
||||||
|
return Ok(withdraw_ixs);
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn serum3_place_order(
|
pub async fn serum3_place_order(
|
||||||
&self,
|
&self,
|
||||||
|
@ -798,9 +1049,12 @@ impl MangoClient {
|
||||||
client_order_id: u64,
|
client_order_id: u64,
|
||||||
limit: u16,
|
limit: u16,
|
||||||
) -> anyhow::Result<Signature> {
|
) -> anyhow::Result<Signature> {
|
||||||
let account = self.mango_account().await?;
|
let mut account = self.mango_account().await?.clone();
|
||||||
let market_index = self.context.serum3_market_index(name);
|
let market_index = self.context.serum3_market_index(name);
|
||||||
let ixs = self
|
let create_or_replace_ixs = self
|
||||||
|
.serum3_create_or_replace_account_instruction(&mut account, market_index, side)
|
||||||
|
.await?;
|
||||||
|
let place_order_ixs = self
|
||||||
.serum3_place_order_instruction(
|
.serum3_place_order_instruction(
|
||||||
&account,
|
&account,
|
||||||
market_index,
|
market_index,
|
||||||
|
@ -814,6 +1068,10 @@ impl MangoClient {
|
||||||
limit,
|
limit,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let mut ixs = PreparedInstructions::new();
|
||||||
|
ixs.append(create_or_replace_ixs);
|
||||||
|
ixs.append(place_order_ixs);
|
||||||
self.send_and_confirm_owner_tx(ixs.to_instructions()).await
|
self.send_and_confirm_owner_tx(ixs.to_instructions()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1999,6 +2257,7 @@ pub enum FallbackOracleConfig {
|
||||||
/// Every possible fallback oracle (may cause serious issues with the 64 accounts-per-tx limit)
|
/// Every possible fallback oracle (may cause serious issues with the 64 accounts-per-tx limit)
|
||||||
All,
|
All,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FallbackOracleConfig {
|
impl Default for FallbackOracleConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
FallbackOracleConfig::Dynamic
|
FallbackOracleConfig::Dynamic
|
||||||
|
|
Loading…
Reference in New Issue