Serum: Settle funds instruction
Also move serum3 cpi helpers to a separate file, to allow reuse of calls like settle_funds from multiple mango instructions.
This commit is contained in:
parent
4987e072cd
commit
70316fb927
|
@ -8,6 +8,7 @@ pub use register_token::*;
|
||||||
pub use serum3_create_open_orders::*;
|
pub use serum3_create_open_orders::*;
|
||||||
pub use serum3_place_order::*;
|
pub use serum3_place_order::*;
|
||||||
pub use serum3_register_market::*;
|
pub use serum3_register_market::*;
|
||||||
|
pub use serum3_settle_funds::*;
|
||||||
pub use set_stub_oracle::*;
|
pub use set_stub_oracle::*;
|
||||||
pub use withdraw::*;
|
pub use withdraw::*;
|
||||||
|
|
||||||
|
@ -21,5 +22,6 @@ mod register_token;
|
||||||
mod serum3_create_open_orders;
|
mod serum3_create_open_orders;
|
||||||
mod serum3_place_order;
|
mod serum3_place_order;
|
||||||
mod serum3_register_market;
|
mod serum3_register_market;
|
||||||
|
mod serum3_settle_funds;
|
||||||
mod set_stub_oracle;
|
mod set_stub_oracle;
|
||||||
mod withdraw;
|
mod withdraw;
|
||||||
|
|
|
@ -160,7 +160,6 @@ pub struct Serum3PlaceOrder<'info> {
|
||||||
pub base_vault: Box<Account<'info, TokenAccount>>,
|
pub base_vault: Box<Account<'info, TokenAccount>>,
|
||||||
|
|
||||||
pub token_program: Program<'info, Token>,
|
pub token_program: Program<'info, Token>,
|
||||||
pub rent: Sysvar<'info, Rent>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serum3_place_order(
|
pub fn serum3_place_order(
|
||||||
|
@ -219,8 +218,8 @@ pub fn serum3_place_order(
|
||||||
// Apply the order to serum. Also immediately settle, in case the order
|
// Apply the order to serum. Also immediately settle, in case the order
|
||||||
// matched against an existing other order.
|
// matched against an existing other order.
|
||||||
//
|
//
|
||||||
cpi_place_order(&ctx, order.0)?;
|
cpi_place_order(&ctx.accounts, order.0)?;
|
||||||
cpi_settle_funds(&ctx)?;
|
cpi_settle_funds(&ctx.accounts)?;
|
||||||
|
|
||||||
//
|
//
|
||||||
// After-order tracking
|
// After-order tracking
|
||||||
|
@ -257,90 +256,56 @@ pub fn serum3_place_order(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cpi_place_order(ctx: &Context<Serum3PlaceOrder>, order: NewOrderInstructionV3) -> Result<()> {
|
fn cpi_place_order(ctx: &Serum3PlaceOrder, order: NewOrderInstructionV3) -> Result<()> {
|
||||||
|
use crate::serum3_cpi;
|
||||||
|
|
||||||
let order_payer_token_account = match order.side {
|
let order_payer_token_account = match order.side {
|
||||||
Side::Bid => &ctx.accounts.quote_vault,
|
Side::Bid => &ctx.quote_vault,
|
||||||
Side::Ask => &ctx.accounts.base_vault,
|
Side::Ask => &ctx.base_vault,
|
||||||
};
|
};
|
||||||
|
|
||||||
let data = serum_dex::instruction::MarketInstruction::NewOrderV3(order).pack();
|
let group = ctx.group.load()?;
|
||||||
let instruction = solana_program::instruction::Instruction {
|
serum3_cpi::place_order(
|
||||||
program_id: *ctx.accounts.serum_program.key,
|
&group,
|
||||||
data,
|
serum3_cpi::PlaceOrder {
|
||||||
accounts: vec![
|
program: ctx.serum_program.to_account_info(),
|
||||||
AccountMeta::new(*ctx.accounts.serum_market_external.key, false),
|
market: ctx.serum_market_external.to_account_info(),
|
||||||
AccountMeta::new(*ctx.accounts.open_orders.key, false),
|
request_queue: ctx.market_request_queue.to_account_info(),
|
||||||
AccountMeta::new(*ctx.accounts.market_request_queue.key, false),
|
event_queue: ctx.market_event_queue.to_account_info(),
|
||||||
AccountMeta::new(*ctx.accounts.market_event_queue.key, false),
|
bids: ctx.market_bids.to_account_info(),
|
||||||
AccountMeta::new(*ctx.accounts.market_bids.key, false),
|
asks: ctx.market_asks.to_account_info(),
|
||||||
AccountMeta::new(*ctx.accounts.market_asks.key, false),
|
base_vault: ctx.market_base_vault.to_account_info(),
|
||||||
AccountMeta::new(order_payer_token_account.key(), false),
|
quote_vault: ctx.market_quote_vault.to_account_info(),
|
||||||
AccountMeta::new_readonly(ctx.accounts.group.key(), true),
|
token_program: ctx.token_program.to_account_info(),
|
||||||
AccountMeta::new(*ctx.accounts.market_base_vault.key, false),
|
|
||||||
AccountMeta::new(*ctx.accounts.market_quote_vault.key, false),
|
|
||||||
AccountMeta::new_readonly(*ctx.accounts.token_program.key, false),
|
|
||||||
AccountMeta::new_readonly(ctx.accounts.group.key(), false),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
let account_infos = [
|
|
||||||
ctx.accounts.serum_program.to_account_info(), // Have to add account of the program id
|
|
||||||
ctx.accounts.serum_market_external.to_account_info(),
|
|
||||||
ctx.accounts.open_orders.to_account_info(),
|
|
||||||
ctx.accounts.market_request_queue.to_account_info(),
|
|
||||||
ctx.accounts.market_event_queue.to_account_info(),
|
|
||||||
ctx.accounts.market_bids.to_account_info(),
|
|
||||||
ctx.accounts.market_asks.to_account_info(),
|
|
||||||
order_payer_token_account.to_account_info(),
|
|
||||||
ctx.accounts.group.to_account_info(),
|
|
||||||
ctx.accounts.market_base_vault.to_account_info(),
|
|
||||||
ctx.accounts.market_quote_vault.to_account_info(),
|
|
||||||
ctx.accounts.token_program.to_account_info(),
|
|
||||||
ctx.accounts.group.to_account_info(),
|
|
||||||
];
|
|
||||||
|
|
||||||
let group = ctx.accounts.group.load()?;
|
open_orders: ctx.open_orders.to_account_info(),
|
||||||
let seeds = group_seeds!(group);
|
order_payer_token_account: order_payer_token_account.to_account_info(),
|
||||||
solana_program::program::invoke_signed_unchecked(&instruction, &account_infos, &[seeds])?;
|
user_authority: ctx.group.to_account_info(),
|
||||||
|
},
|
||||||
|
order,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cpi_settle_funds(ctx: &Context<Serum3PlaceOrder>) -> Result<()> {
|
fn cpi_settle_funds(ctx: &Serum3PlaceOrder) -> Result<()> {
|
||||||
let data = serum_dex::instruction::MarketInstruction::SettleFunds.pack();
|
use crate::serum3_cpi;
|
||||||
let instruction = solana_program::instruction::Instruction {
|
let group = ctx.group.load()?;
|
||||||
program_id: *ctx.accounts.serum_program.key,
|
serum3_cpi::settle_funds(
|
||||||
data,
|
&group,
|
||||||
accounts: vec![
|
serum3_cpi::SettleFunds {
|
||||||
AccountMeta::new(*ctx.accounts.serum_market_external.key, false),
|
program: ctx.serum_program.to_account_info(),
|
||||||
AccountMeta::new(*ctx.accounts.open_orders.key, false),
|
market: ctx.serum_market_external.to_account_info(),
|
||||||
AccountMeta::new_readonly(ctx.accounts.group.key(), true),
|
open_orders: ctx.open_orders.to_account_info(),
|
||||||
AccountMeta::new(*ctx.accounts.market_base_vault.key, false),
|
open_orders_authority: ctx.group.to_account_info(),
|
||||||
AccountMeta::new(*ctx.accounts.market_quote_vault.key, false),
|
base_vault: ctx.market_base_vault.to_account_info(),
|
||||||
AccountMeta::new(ctx.accounts.base_vault.key(), false),
|
quote_vault: ctx.market_quote_vault.to_account_info(),
|
||||||
AccountMeta::new(ctx.accounts.quote_vault.key(), false),
|
user_base_wallet: ctx.base_vault.to_account_info(),
|
||||||
AccountMeta::new_readonly(*ctx.accounts.market_vault_signer.key, false),
|
user_quote_wallet: ctx.quote_vault.to_account_info(),
|
||||||
AccountMeta::new_readonly(*ctx.accounts.token_program.key, false),
|
vault_signer: ctx.market_vault_signer.to_account_info(),
|
||||||
AccountMeta::new(ctx.accounts.quote_vault.key(), false),
|
token_program: ctx.token_program.to_account_info(),
|
||||||
],
|
},
|
||||||
};
|
)?;
|
||||||
|
|
||||||
let account_infos = [
|
|
||||||
ctx.accounts.serum_market_external.to_account_info(),
|
|
||||||
ctx.accounts.serum_market_external.to_account_info(),
|
|
||||||
ctx.accounts.open_orders.to_account_info(),
|
|
||||||
ctx.accounts.group.to_account_info(),
|
|
||||||
ctx.accounts.market_base_vault.to_account_info(),
|
|
||||||
ctx.accounts.market_quote_vault.to_account_info(),
|
|
||||||
ctx.accounts.base_vault.to_account_info(),
|
|
||||||
ctx.accounts.quote_vault.to_account_info(),
|
|
||||||
ctx.accounts.market_vault_signer.to_account_info(),
|
|
||||||
ctx.accounts.token_program.to_account_info(),
|
|
||||||
ctx.accounts.quote_vault.to_account_info(),
|
|
||||||
];
|
|
||||||
|
|
||||||
let group = ctx.accounts.group.load()?;
|
|
||||||
let seeds = group_seeds!(group);
|
|
||||||
solana_program::program::invoke_signed_unchecked(&instruction, &account_infos, &[seeds])?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
use anchor_spl::token::{Token, TokenAccount};
|
||||||
|
|
||||||
|
use crate::error::*;
|
||||||
|
use crate::state::*;
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
pub struct Serum3SettleFunds<'info> {
|
||||||
|
pub group: AccountLoader<'info, Group>,
|
||||||
|
|
||||||
|
#[account(
|
||||||
|
mut,
|
||||||
|
has_one = group,
|
||||||
|
has_one = owner,
|
||||||
|
)]
|
||||||
|
pub account: AccountLoader<'info, MangoAccount>,
|
||||||
|
pub owner: Signer<'info>,
|
||||||
|
|
||||||
|
// Validated inline
|
||||||
|
#[account(mut)]
|
||||||
|
pub open_orders: UncheckedAccount<'info>,
|
||||||
|
|
||||||
|
#[account(
|
||||||
|
has_one = group,
|
||||||
|
has_one = serum_program,
|
||||||
|
has_one = serum_market_external,
|
||||||
|
)]
|
||||||
|
pub serum_market: AccountLoader<'info, Serum3Market>,
|
||||||
|
pub serum_program: UncheckedAccount<'info>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub serum_market_external: UncheckedAccount<'info>,
|
||||||
|
|
||||||
|
// These accounts are forwarded directly to the serum cpi call
|
||||||
|
// and are validated there.
|
||||||
|
#[account(mut)]
|
||||||
|
pub market_base_vault: UncheckedAccount<'info>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub market_quote_vault: UncheckedAccount<'info>,
|
||||||
|
// needed for the automatic settle_funds call
|
||||||
|
pub market_vault_signer: UncheckedAccount<'info>,
|
||||||
|
|
||||||
|
// TODO: do we need to pass both, or just payer?
|
||||||
|
// TODO: if we potentially settle immediately, they all need to be mut?
|
||||||
|
// TODO: Can we reduce the number of accounts by requiring the banks
|
||||||
|
// to be in the remainingAccounts (where they need to be anyway, for
|
||||||
|
// health checks - but they need to be mut)
|
||||||
|
// Validated inline
|
||||||
|
#[account(mut)]
|
||||||
|
pub quote_bank: AccountLoader<'info, Bank>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub quote_vault: Box<Account<'info, TokenAccount>>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub base_bank: AccountLoader<'info, Bank>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub base_vault: Box<Account<'info, TokenAccount>>,
|
||||||
|
|
||||||
|
pub token_program: Program<'info, Token>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serum3_settle_funds(ctx: Context<Serum3SettleFunds>) -> Result<()> {
|
||||||
|
//
|
||||||
|
// Validation
|
||||||
|
//
|
||||||
|
{
|
||||||
|
let account = ctx.accounts.account.load()?;
|
||||||
|
let serum_market = ctx.accounts.serum_market.load()?;
|
||||||
|
|
||||||
|
// Validate open_orders
|
||||||
|
require!(
|
||||||
|
account
|
||||||
|
.serum3_account_map
|
||||||
|
.find(serum_market.market_index)
|
||||||
|
.ok_or(error!(MangoError::SomeError))?
|
||||||
|
.open_orders
|
||||||
|
== ctx.accounts.open_orders.key(),
|
||||||
|
MangoError::SomeError
|
||||||
|
);
|
||||||
|
|
||||||
|
// Validate banks and vaults
|
||||||
|
let quote_bank = ctx.accounts.quote_bank.load()?;
|
||||||
|
require!(
|
||||||
|
quote_bank.vault == ctx.accounts.quote_vault.key(),
|
||||||
|
MangoError::SomeError
|
||||||
|
);
|
||||||
|
require!(
|
||||||
|
quote_bank.token_index == serum_market.quote_token_index,
|
||||||
|
MangoError::SomeError
|
||||||
|
);
|
||||||
|
let base_bank = ctx.accounts.base_bank.load()?;
|
||||||
|
require!(
|
||||||
|
base_bank.vault == ctx.accounts.base_vault.key(),
|
||||||
|
MangoError::SomeError
|
||||||
|
);
|
||||||
|
require!(
|
||||||
|
base_bank.token_index == serum_market.base_token_index,
|
||||||
|
MangoError::SomeError
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Before-order tracking
|
||||||
|
//
|
||||||
|
|
||||||
|
let before_base_vault = ctx.accounts.base_vault.amount;
|
||||||
|
let before_quote_vault = ctx.accounts.quote_vault.amount;
|
||||||
|
|
||||||
|
// TODO: pre-health check
|
||||||
|
|
||||||
|
//
|
||||||
|
// Settle
|
||||||
|
//
|
||||||
|
cpi_settle_funds(&ctx.accounts)?;
|
||||||
|
|
||||||
|
//
|
||||||
|
// After-order tracking
|
||||||
|
//
|
||||||
|
ctx.accounts.base_vault.reload()?;
|
||||||
|
ctx.accounts.quote_vault.reload()?;
|
||||||
|
let after_base_vault = ctx.accounts.base_vault.amount;
|
||||||
|
let after_quote_vault = ctx.accounts.quote_vault.amount;
|
||||||
|
|
||||||
|
// Charge the difference in vault balances to the user's account
|
||||||
|
{
|
||||||
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
|
|
||||||
|
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
|
||||||
|
let base_position = account.token_account_map.get_mut(base_bank.token_index)?;
|
||||||
|
base_bank.change(base_position, (after_base_vault - before_base_vault) as i64)?;
|
||||||
|
|
||||||
|
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
|
||||||
|
let quote_position = account.token_account_map.get_mut(quote_bank.token_index)?;
|
||||||
|
quote_bank.change(
|
||||||
|
quote_position,
|
||||||
|
(after_quote_vault - before_quote_vault) as i64,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Health check
|
||||||
|
//
|
||||||
|
let account = ctx.accounts.account.load()?;
|
||||||
|
let health = compute_health(&account, &ctx.remaining_accounts)?;
|
||||||
|
msg!("health: {}", health);
|
||||||
|
require!(health >= 0, MangoError::SomeError);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpi_settle_funds(ctx: &Serum3SettleFunds) -> Result<()> {
|
||||||
|
use crate::serum3_cpi;
|
||||||
|
let group = ctx.group.load()?;
|
||||||
|
serum3_cpi::settle_funds(
|
||||||
|
&group,
|
||||||
|
serum3_cpi::SettleFunds {
|
||||||
|
program: ctx.serum_program.to_account_info(),
|
||||||
|
market: ctx.serum_market_external.to_account_info(),
|
||||||
|
open_orders: ctx.open_orders.to_account_info(),
|
||||||
|
open_orders_authority: ctx.group.to_account_info(),
|
||||||
|
base_vault: ctx.market_base_vault.to_account_info(),
|
||||||
|
quote_vault: ctx.market_quote_vault.to_account_info(),
|
||||||
|
user_base_wallet: ctx.base_vault.to_account_info(),
|
||||||
|
user_quote_wallet: ctx.quote_vault.to_account_info(),
|
||||||
|
vault_signer: ctx.market_vault_signer.to_account_info(),
|
||||||
|
token_program: ctx.token_program.to_account_info(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ use instructions::*;
|
||||||
pub mod address_lookup_table;
|
pub mod address_lookup_table;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod instructions;
|
pub mod instructions;
|
||||||
|
mod serum3_cpi;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
|
||||||
use state::{Serum3MarketIndex, TokenIndex};
|
use state::{Serum3MarketIndex, TokenIndex};
|
||||||
|
@ -97,6 +98,10 @@ pub mod mango_v4 {
|
||||||
instructions::serum3_place_order(ctx, order)
|
instructions::serum3_place_order(ctx, order)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn serum3_settle_funds(ctx: Context<Serum3SettleFunds>) -> Result<()> {
|
||||||
|
instructions::serum3_settle_funds(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_perp_market(
|
pub fn create_perp_market(
|
||||||
ctx: Context<CreatePerpMarket>,
|
ctx: Context<CreatePerpMarket>,
|
||||||
quote_lot_size: i64,
|
quote_lot_size: i64,
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
use anchor_spl::dex::serum_dex;
|
||||||
|
|
||||||
|
use crate::state::*;
|
||||||
|
|
||||||
|
pub struct SettleFunds<'info> {
|
||||||
|
pub program: AccountInfo<'info>,
|
||||||
|
pub market: AccountInfo<'info>,
|
||||||
|
pub open_orders: AccountInfo<'info>,
|
||||||
|
pub open_orders_authority: AccountInfo<'info>,
|
||||||
|
pub base_vault: AccountInfo<'info>,
|
||||||
|
pub quote_vault: AccountInfo<'info>,
|
||||||
|
pub user_base_wallet: AccountInfo<'info>,
|
||||||
|
pub user_quote_wallet: AccountInfo<'info>,
|
||||||
|
pub vault_signer: AccountInfo<'info>,
|
||||||
|
pub token_program: AccountInfo<'info>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn settle_funds(group: &Group, ctx: SettleFunds) -> Result<()> {
|
||||||
|
let data = serum_dex::instruction::MarketInstruction::SettleFunds.pack();
|
||||||
|
let instruction = solana_program::instruction::Instruction {
|
||||||
|
program_id: *ctx.program.key,
|
||||||
|
data,
|
||||||
|
accounts: vec![
|
||||||
|
AccountMeta::new(*ctx.market.key, false),
|
||||||
|
AccountMeta::new(*ctx.open_orders.key, false),
|
||||||
|
AccountMeta::new_readonly(*ctx.open_orders_authority.key, true),
|
||||||
|
AccountMeta::new(*ctx.base_vault.key, false),
|
||||||
|
AccountMeta::new(*ctx.quote_vault.key, false),
|
||||||
|
AccountMeta::new(*ctx.user_base_wallet.key, false),
|
||||||
|
AccountMeta::new(*ctx.user_quote_wallet.key, false),
|
||||||
|
AccountMeta::new_readonly(*ctx.vault_signer.key, false),
|
||||||
|
AccountMeta::new_readonly(*ctx.token_program.key, false),
|
||||||
|
AccountMeta::new(*ctx.user_quote_wallet.key, false),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let account_infos = [
|
||||||
|
ctx.program,
|
||||||
|
ctx.market,
|
||||||
|
ctx.open_orders,
|
||||||
|
ctx.open_orders_authority,
|
||||||
|
ctx.base_vault,
|
||||||
|
ctx.quote_vault,
|
||||||
|
ctx.user_base_wallet,
|
||||||
|
ctx.user_quote_wallet.clone(),
|
||||||
|
ctx.vault_signer,
|
||||||
|
ctx.token_program,
|
||||||
|
ctx.user_quote_wallet,
|
||||||
|
];
|
||||||
|
|
||||||
|
let seeds = group_seeds!(group);
|
||||||
|
solana_program::program::invoke_signed_unchecked(&instruction, &account_infos, &[seeds])?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PlaceOrder<'info> {
|
||||||
|
pub program: AccountInfo<'info>,
|
||||||
|
pub market: AccountInfo<'info>,
|
||||||
|
pub request_queue: AccountInfo<'info>,
|
||||||
|
pub event_queue: AccountInfo<'info>,
|
||||||
|
pub bids: AccountInfo<'info>,
|
||||||
|
pub asks: AccountInfo<'info>,
|
||||||
|
pub base_vault: AccountInfo<'info>,
|
||||||
|
pub quote_vault: AccountInfo<'info>,
|
||||||
|
pub token_program: AccountInfo<'info>,
|
||||||
|
|
||||||
|
pub open_orders: AccountInfo<'info>,
|
||||||
|
pub order_payer_token_account: AccountInfo<'info>,
|
||||||
|
// must cover the open_orders and the order_payer_token_account
|
||||||
|
pub user_authority: AccountInfo<'info>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn place_order(
|
||||||
|
group: &Group,
|
||||||
|
ctx: PlaceOrder,
|
||||||
|
order: serum_dex::instruction::NewOrderInstructionV3,
|
||||||
|
) -> Result<()> {
|
||||||
|
let data = serum_dex::instruction::MarketInstruction::NewOrderV3(order).pack();
|
||||||
|
let instruction = solana_program::instruction::Instruction {
|
||||||
|
program_id: *ctx.program.key,
|
||||||
|
data,
|
||||||
|
accounts: vec![
|
||||||
|
AccountMeta::new(*ctx.market.key, false),
|
||||||
|
AccountMeta::new(*ctx.open_orders.key, false),
|
||||||
|
AccountMeta::new(*ctx.request_queue.key, false),
|
||||||
|
AccountMeta::new(*ctx.event_queue.key, false),
|
||||||
|
AccountMeta::new(*ctx.bids.key, false),
|
||||||
|
AccountMeta::new(*ctx.asks.key, false),
|
||||||
|
AccountMeta::new(*ctx.order_payer_token_account.key, false),
|
||||||
|
AccountMeta::new_readonly(*ctx.user_authority.key, true),
|
||||||
|
AccountMeta::new(*ctx.base_vault.key, false),
|
||||||
|
AccountMeta::new(*ctx.quote_vault.key, false),
|
||||||
|
AccountMeta::new_readonly(*ctx.token_program.key, false),
|
||||||
|
AccountMeta::new_readonly(*ctx.user_authority.key, false),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let account_infos = [
|
||||||
|
ctx.program,
|
||||||
|
ctx.market,
|
||||||
|
ctx.open_orders,
|
||||||
|
ctx.request_queue,
|
||||||
|
ctx.event_queue,
|
||||||
|
ctx.bids,
|
||||||
|
ctx.asks,
|
||||||
|
ctx.order_payer_token_account,
|
||||||
|
ctx.user_authority.clone(),
|
||||||
|
ctx.base_vault,
|
||||||
|
ctx.quote_vault,
|
||||||
|
ctx.token_program,
|
||||||
|
ctx.user_authority,
|
||||||
|
];
|
||||||
|
|
||||||
|
let seeds = group_seeds!(group);
|
||||||
|
solana_program::program::invoke_signed_unchecked(&instruction, &account_infos, &[seeds])?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -839,7 +839,87 @@ impl<'keypair> ClientInstruction for Serum3PlaceOrderInstruction<'keypair> {
|
||||||
market_vault_signer: vault_signer,
|
market_vault_signer: vault_signer,
|
||||||
owner: self.owner.pubkey(),
|
owner: self.owner.pubkey(),
|
||||||
token_program: Token::id(),
|
token_program: Token::id(),
|
||||||
rent: sysvar::rent::Rent::id(),
|
};
|
||||||
|
|
||||||
|
let mut instruction = make_instruction(program_id, &accounts, instruction);
|
||||||
|
instruction.accounts.extend(health_check_metas.into_iter());
|
||||||
|
|
||||||
|
(accounts, instruction)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signers(&self) -> Vec<&Keypair> {
|
||||||
|
vec![self.owner]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Serum3SettleFundsInstruction<'keypair> {
|
||||||
|
pub account: Pubkey,
|
||||||
|
pub owner: &'keypair Keypair,
|
||||||
|
|
||||||
|
pub serum_market: Pubkey,
|
||||||
|
}
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl<'keypair> ClientInstruction for Serum3SettleFundsInstruction<'keypair> {
|
||||||
|
type Accounts = mango_v4::accounts::Serum3SettleFunds;
|
||||||
|
type Instruction = mango_v4::instruction::Serum3SettleFunds;
|
||||||
|
async fn to_instruction(
|
||||||
|
&self,
|
||||||
|
account_loader: impl ClientAccountLoader + 'async_trait,
|
||||||
|
) -> (Self::Accounts, instruction::Instruction) {
|
||||||
|
let program_id = mango_v4::id();
|
||||||
|
let instruction = Self::Instruction {};
|
||||||
|
|
||||||
|
let account: MangoAccount = account_loader.load(&self.account).await.unwrap();
|
||||||
|
let serum_market: Serum3Market = account_loader.load(&self.serum_market).await.unwrap();
|
||||||
|
let open_orders = account
|
||||||
|
.serum3_account_map
|
||||||
|
.find(serum_market.market_index)
|
||||||
|
.unwrap()
|
||||||
|
.open_orders;
|
||||||
|
let quote_info =
|
||||||
|
get_mint_info_by_token_index(&account_loader, &account, serum_market.quote_token_index)
|
||||||
|
.await;
|
||||||
|
let base_info =
|
||||||
|
get_mint_info_by_token_index(&account_loader, &account, serum_market.base_token_index)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let market_external_bytes = account_loader
|
||||||
|
.load_bytes(&serum_market.serum_market_external)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let market_external: &serum_dex::state::MarketState = bytemuck::from_bytes(
|
||||||
|
&market_external_bytes[5..5 + std::mem::size_of::<serum_dex::state::MarketState>()],
|
||||||
|
);
|
||||||
|
// unpack the data, to avoid unaligned references
|
||||||
|
let coin_vault = market_external.coin_vault;
|
||||||
|
let pc_vault = market_external.pc_vault;
|
||||||
|
let vault_signer = serum_dex::state::gen_vault_signer_key(
|
||||||
|
market_external.vault_signer_nonce,
|
||||||
|
&serum_market.serum_market_external,
|
||||||
|
&serum_market.serum_program,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let health_check_metas =
|
||||||
|
derive_health_check_remaining_account_metas(&account_loader, &account, None, false)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let accounts = Self::Accounts {
|
||||||
|
group: account.group,
|
||||||
|
account: self.account,
|
||||||
|
open_orders,
|
||||||
|
quote_bank: quote_info.bank,
|
||||||
|
quote_vault: quote_info.vault,
|
||||||
|
base_bank: base_info.bank,
|
||||||
|
base_vault: base_info.vault,
|
||||||
|
serum_market: self.serum_market,
|
||||||
|
serum_program: serum_market.serum_program,
|
||||||
|
serum_market_external: serum_market.serum_market_external,
|
||||||
|
market_base_vault: from_serum_style_pubkey(&coin_vault),
|
||||||
|
market_quote_vault: from_serum_style_pubkey(&pc_vault),
|
||||||
|
market_vault_signer: vault_signer,
|
||||||
|
owner: self.owner.pubkey(),
|
||||||
|
token_program: Token::id(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut instruction = make_instruction(program_id, &accounts, instruction);
|
let mut instruction = make_instruction(program_id, &accounts, instruction);
|
||||||
|
|
|
@ -208,5 +208,17 @@ async fn test_serum() -> Result<(), TransportError> {
|
||||||
assert_eq!(native0, 1000);
|
assert_eq!(native0, 1000);
|
||||||
assert_eq!(native1, 900);
|
assert_eq!(native1, 900);
|
||||||
|
|
||||||
|
// TODO: Currently has no effect
|
||||||
|
send_tx(
|
||||||
|
solana,
|
||||||
|
Serum3SettleFundsInstruction {
|
||||||
|
account,
|
||||||
|
owner,
|
||||||
|
serum_market,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue