create perp market wip
Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
af2f4a0923
commit
4f7e3a5738
|
@ -0,0 +1,84 @@
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
|
use crate::error::MangoError;
|
||||||
|
use crate::state::*;
|
||||||
|
use anchor_spl::token::Token;
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
pub struct CreatePerpMarket<'info> {
|
||||||
|
#[account(
|
||||||
|
mut,
|
||||||
|
has_one = admin,
|
||||||
|
)]
|
||||||
|
pub group: AccountLoader<'info, Group>,
|
||||||
|
pub admin: Signer<'info>,
|
||||||
|
|
||||||
|
pub oracle: UncheckedAccount<'info>,
|
||||||
|
#[account(
|
||||||
|
init,
|
||||||
|
seeds = [group.key().as_ref(), b"PerpMarket".as_ref(), oracle.key().as_ref()],
|
||||||
|
bump,
|
||||||
|
payer = payer,
|
||||||
|
space = 8 + std::mem::size_of::<PerpMarket>(),
|
||||||
|
)]
|
||||||
|
pub perp_market: AccountLoader<'info, PerpMarket>,
|
||||||
|
#[account(
|
||||||
|
init,
|
||||||
|
seeds = [group.key().as_ref(), b"Asks".as_ref(), perp_market.key().as_ref()],
|
||||||
|
bump,
|
||||||
|
payer = payer,
|
||||||
|
space = 8 + std::mem::size_of::<PerpMarket>(),
|
||||||
|
)]
|
||||||
|
pub asks: AccountLoader<'info, crate::state::Book>,
|
||||||
|
#[account(
|
||||||
|
init,
|
||||||
|
seeds = [group.key().as_ref(), b"Bids".as_ref(), perp_market.key().as_ref()],
|
||||||
|
bump,
|
||||||
|
payer = payer,
|
||||||
|
space = 8 + std::mem::size_of::<PerpMarket>(),
|
||||||
|
)]
|
||||||
|
pub bids: AccountLoader<'info, Book>,
|
||||||
|
#[account(
|
||||||
|
init,
|
||||||
|
seeds = [group.key().as_ref(), b"EventQueue".as_ref(), perp_market.key().as_ref()],
|
||||||
|
bump,
|
||||||
|
payer = payer,
|
||||||
|
space = 8 + std::mem::size_of::<PerpMarket>(),
|
||||||
|
)]
|
||||||
|
pub event_queue: AccountLoader<'info, crate::state::EventQueue>,
|
||||||
|
|
||||||
|
#[account(mut)]
|
||||||
|
pub payer: Signer<'info>,
|
||||||
|
|
||||||
|
pub system_program: Program<'info, System>,
|
||||||
|
pub token_program: Program<'info, Token>,
|
||||||
|
pub rent: Sysvar<'info, Rent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_perp_market(
|
||||||
|
ctx: Context<CreatePerpMarket>,
|
||||||
|
quote_lot_size: i64,
|
||||||
|
base_lot_size: i64,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut perp_market = ctx.accounts.perp_market.load_init()?;
|
||||||
|
*perp_market = PerpMarket {
|
||||||
|
group: ctx.accounts.group.key(),
|
||||||
|
oracle: ctx.accounts.oracle.key(),
|
||||||
|
bids: ctx.accounts.bids.key(),
|
||||||
|
asks: ctx.accounts.asks.key(),
|
||||||
|
event_queue: ctx.accounts.event_queue.key(),
|
||||||
|
quote_lot_size: quote_lot_size,
|
||||||
|
base_lot_size: base_lot_size,
|
||||||
|
// long_funding,
|
||||||
|
// short_funding,
|
||||||
|
// last_updated,
|
||||||
|
// open_interest,
|
||||||
|
seq_num: 0,
|
||||||
|
// fees_accrued,
|
||||||
|
// liquidity_mining_info,
|
||||||
|
// mngo_vault: ctx.accounts.mngo_vault.key(),
|
||||||
|
bump: *ctx.bumps.get("perp_market").ok_or(MangoError::SomeError)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
pub use self::margin_trade::*;
|
pub use self::margin_trade::*;
|
||||||
pub use create_account::*;
|
pub use create_account::*;
|
||||||
pub use create_group::*;
|
pub use create_group::*;
|
||||||
|
pub use create_perp_market::*;
|
||||||
pub use create_serum_open_orders::*;
|
pub use create_serum_open_orders::*;
|
||||||
pub use create_stub_oracle::*;
|
pub use create_stub_oracle::*;
|
||||||
pub use deposit::*;
|
pub use deposit::*;
|
||||||
|
@ -12,6 +13,7 @@ pub use withdraw::*;
|
||||||
|
|
||||||
mod create_account;
|
mod create_account;
|
||||||
mod create_group;
|
mod create_group;
|
||||||
|
mod create_perp_market;
|
||||||
mod create_serum_open_orders;
|
mod create_serum_open_orders;
|
||||||
mod create_stub_oracle;
|
mod create_stub_oracle;
|
||||||
mod deposit;
|
mod deposit;
|
||||||
|
|
|
@ -96,6 +96,14 @@ pub mod mango_v4 {
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
instructions::place_serum_order(ctx, order)
|
instructions::place_serum_order(ctx, order)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_perp_market(
|
||||||
|
ctx: Context<CreatePerpMarket>,
|
||||||
|
quote_lot_size: i64,
|
||||||
|
base_lot_size: i64,
|
||||||
|
) -> Result<()> {
|
||||||
|
instructions::create_perp_market(ctx, quote_lot_size, base_lot_size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -4,6 +4,7 @@ pub use health::*;
|
||||||
pub use mango_account::*;
|
pub use mango_account::*;
|
||||||
pub use mint_info::*;
|
pub use mint_info::*;
|
||||||
pub use oracle::*;
|
pub use oracle::*;
|
||||||
|
pub use perp::*;
|
||||||
pub use serum_market::*;
|
pub use serum_market::*;
|
||||||
|
|
||||||
mod bank;
|
mod bank;
|
||||||
|
@ -12,4 +13,5 @@ mod health;
|
||||||
mod mango_account;
|
mod mango_account;
|
||||||
mod mint_info;
|
mod mint_info;
|
||||||
mod oracle;
|
mod oracle;
|
||||||
|
mod perp;
|
||||||
mod serum_market;
|
mod serum_market;
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
|
#[account(zero_copy)]
|
||||||
|
pub struct Book {}
|
|
@ -0,0 +1,4 @@
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
|
#[account(zero_copy)]
|
||||||
|
pub struct EventQueue {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
pub use book::*;
|
||||||
|
pub use event_queue::*;
|
||||||
|
pub use perp_market::*;
|
||||||
|
|
||||||
|
mod book;
|
||||||
|
mod event_queue;
|
||||||
|
mod perp_market;
|
|
@ -0,0 +1,68 @@
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
|
#[account(zero_copy)]
|
||||||
|
pub struct PerpMarket {
|
||||||
|
// todo
|
||||||
|
/// metadata
|
||||||
|
// pub meta_data: MetaData,
|
||||||
|
|
||||||
|
/// mango group
|
||||||
|
pub group: Pubkey,
|
||||||
|
|
||||||
|
// todo better docs
|
||||||
|
///
|
||||||
|
pub oracle: Pubkey,
|
||||||
|
|
||||||
|
/// order book
|
||||||
|
pub bids: Pubkey,
|
||||||
|
pub asks: Pubkey,
|
||||||
|
|
||||||
|
// todo better docs
|
||||||
|
///
|
||||||
|
pub event_queue: Pubkey,
|
||||||
|
|
||||||
|
/// number of quote native that reresents min tick
|
||||||
|
/// e.g. base lot size 100, quote lot size 10, then tick i.e. price increment is 10/100 i.e. 1
|
||||||
|
// todo: why signed?
|
||||||
|
pub quote_lot_size: i64,
|
||||||
|
/// represents number of base native quantity; greater than 0
|
||||||
|
/// e.g. base decimals 6, base lot size 100, base position 10000, then
|
||||||
|
/// UI position is 1
|
||||||
|
// todo: why signed?
|
||||||
|
pub base_lot_size: i64,
|
||||||
|
|
||||||
|
// todo
|
||||||
|
/// an always increasing number (except in case of socializing losses), incremented by
|
||||||
|
/// funding delta, funding delta is difference between book and index price which needs to be paid every day,
|
||||||
|
/// funding delta is measured per day - per base lots - the larger users position the more funding
|
||||||
|
/// he pays, funding is always paid in quote
|
||||||
|
// pub long_funding: I80F48,
|
||||||
|
// pub short_funding: I80F48,
|
||||||
|
// todo
|
||||||
|
/// timestamp when funding was last updated
|
||||||
|
// pub last_updated: u64,
|
||||||
|
|
||||||
|
// todo
|
||||||
|
/// This is i64 to keep consistent with the units of contracts, but should always be > 0
|
||||||
|
// todo: why signed?
|
||||||
|
// pub open_interest: i64,
|
||||||
|
|
||||||
|
// todo
|
||||||
|
/// number of orders generated
|
||||||
|
pub seq_num: u64,
|
||||||
|
|
||||||
|
// todo
|
||||||
|
/// in native quote currency
|
||||||
|
// pub fees_accrued: I80F48,
|
||||||
|
|
||||||
|
// todo
|
||||||
|
/// liquidity mining
|
||||||
|
// pub liquidity_mining_info: LiquidityMiningInfo,
|
||||||
|
|
||||||
|
// todo
|
||||||
|
/// token vault which holds mango tokens to be disbursed as liquidity incentives for this perp market
|
||||||
|
// pub mngo_vault: Pubkey,
|
||||||
|
|
||||||
|
/// pda bump
|
||||||
|
pub bump: u8,
|
||||||
|
}
|
|
@ -845,3 +845,79 @@ impl<'keypair> ClientInstruction for PlaceSerumOrderInstruction<'keypair> {
|
||||||
vec![self.owner]
|
vec![self.owner]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CreatePerpMarketInstruction<'keypair> {
|
||||||
|
pub group: Pubkey,
|
||||||
|
pub mint: Pubkey,
|
||||||
|
pub admin: &'keypair Keypair,
|
||||||
|
pub payer: &'keypair Keypair,
|
||||||
|
pub quote_lot_size: i64,
|
||||||
|
pub base_lot_size: i64,
|
||||||
|
}
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl<'keypair> ClientInstruction for CreatePerpMarketInstruction<'keypair> {
|
||||||
|
type Accounts = mango_v4::accounts::CreatePerpMarket;
|
||||||
|
type Instruction = mango_v4::instruction::CreatePerpMarket;
|
||||||
|
async fn to_instruction(
|
||||||
|
&self,
|
||||||
|
_loader: impl ClientAccountLoader + 'async_trait,
|
||||||
|
) -> (Self::Accounts, instruction::Instruction) {
|
||||||
|
let program_id = mango_v4::id();
|
||||||
|
let instruction = Self::Instruction {
|
||||||
|
quote_lot_size: self.quote_lot_size,
|
||||||
|
base_lot_size: self.base_lot_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
let oracle = Pubkey::find_program_address(
|
||||||
|
&[b"StubOracle".as_ref(), self.mint.as_ref()],
|
||||||
|
&program_id,
|
||||||
|
)
|
||||||
|
.0;
|
||||||
|
|
||||||
|
let perp_market = Pubkey::find_program_address(
|
||||||
|
&[self.group.as_ref(), b"PerpMarket".as_ref(), oracle.as_ref()],
|
||||||
|
&program_id,
|
||||||
|
)
|
||||||
|
.0;
|
||||||
|
let asks = Pubkey::find_program_address(
|
||||||
|
&[self.group.as_ref(), b"Asks".as_ref(), perp_market.as_ref()],
|
||||||
|
&program_id,
|
||||||
|
)
|
||||||
|
.0;
|
||||||
|
let bids = Pubkey::find_program_address(
|
||||||
|
&[self.group.as_ref(), b"Bids".as_ref(), perp_market.as_ref()],
|
||||||
|
&program_id,
|
||||||
|
)
|
||||||
|
.0;
|
||||||
|
let event_queue = Pubkey::find_program_address(
|
||||||
|
&[
|
||||||
|
self.group.as_ref(),
|
||||||
|
b"EventQueue".as_ref(),
|
||||||
|
perp_market.as_ref(),
|
||||||
|
],
|
||||||
|
&program_id,
|
||||||
|
)
|
||||||
|
.0;
|
||||||
|
|
||||||
|
let accounts = Self::Accounts {
|
||||||
|
group: self.group,
|
||||||
|
admin: self.admin.pubkey(),
|
||||||
|
oracle,
|
||||||
|
perp_market,
|
||||||
|
asks,
|
||||||
|
bids,
|
||||||
|
event_queue,
|
||||||
|
payer: self.payer.pubkey(),
|
||||||
|
system_program: System::id(),
|
||||||
|
token_program: Token::id(),
|
||||||
|
rent: sysvar::rent::Rent::id(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let instruction = make_instruction(program_id, &accounts, instruction);
|
||||||
|
(accounts, instruction)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signers(&self) -> Vec<&Keypair> {
|
||||||
|
vec![self.admin, self.payer]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
#![cfg(feature = "test-bpf")]
|
||||||
|
|
||||||
|
use solana_program::pubkey::Pubkey;
|
||||||
|
use solana_program_test::*;
|
||||||
|
use solana_sdk::{signature::Keypair, transport::TransportError};
|
||||||
|
|
||||||
|
use mango_v4::{instructions::CreatePerpMarket, state::*};
|
||||||
|
use program_test::*;
|
||||||
|
|
||||||
|
mod program_test;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_perp() -> Result<(), TransportError> {
|
||||||
|
let context = TestContext::new(Option::None, Option::None, Option::None, Option::None).await;
|
||||||
|
let solana = &context.solana.clone();
|
||||||
|
|
||||||
|
let admin = &Keypair::new();
|
||||||
|
let owner = &context.users[0].key;
|
||||||
|
let payer = &context.users[1].key;
|
||||||
|
let mint0 = &context.mints[0];
|
||||||
|
let mint1 = &context.mints[1];
|
||||||
|
let payer_mint_accounts = &context.users[1].token_accounts[0..=2];
|
||||||
|
|
||||||
|
//
|
||||||
|
// SETUP: Create a group and an account
|
||||||
|
//
|
||||||
|
|
||||||
|
let group = send_tx(solana, CreateGroupInstruction { admin, payer })
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.group;
|
||||||
|
|
||||||
|
let account = send_tx(
|
||||||
|
solana,
|
||||||
|
CreateAccountInstruction {
|
||||||
|
account_num: 0,
|
||||||
|
group,
|
||||||
|
owner,
|
||||||
|
payer,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.account;
|
||||||
|
|
||||||
|
//
|
||||||
|
// SETUP: Register mints (and make oracles for them)
|
||||||
|
//
|
||||||
|
|
||||||
|
let register_mint = |index: TokenIndex, mint: MintCookie, address_lookup_table: Pubkey| async move {
|
||||||
|
let create_stub_oracle_accounts = send_tx(
|
||||||
|
solana,
|
||||||
|
CreateStubOracle {
|
||||||
|
mint: mint.pubkey,
|
||||||
|
payer,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let oracle = create_stub_oracle_accounts.oracle;
|
||||||
|
send_tx(
|
||||||
|
solana,
|
||||||
|
SetStubOracle {
|
||||||
|
mint: mint.pubkey,
|
||||||
|
payer,
|
||||||
|
price: "1.0",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let register_token_accounts = send_tx(
|
||||||
|
solana,
|
||||||
|
RegisterTokenInstruction {
|
||||||
|
token_index: index,
|
||||||
|
decimals: mint.decimals,
|
||||||
|
maint_asset_weight: 0.9,
|
||||||
|
init_asset_weight: 0.8,
|
||||||
|
maint_liab_weight: 1.1,
|
||||||
|
init_liab_weight: 1.2,
|
||||||
|
group,
|
||||||
|
admin,
|
||||||
|
mint: mint.pubkey,
|
||||||
|
address_lookup_table,
|
||||||
|
payer,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let bank = register_token_accounts.bank;
|
||||||
|
|
||||||
|
(oracle, bank)
|
||||||
|
};
|
||||||
|
|
||||||
|
let address_lookup_table = solana.create_address_lookup_table(admin, payer).await;
|
||||||
|
let base_token_index = 0;
|
||||||
|
let (_oracle0, _bank0) =
|
||||||
|
register_mint(base_token_index, mint0.clone(), address_lookup_table).await;
|
||||||
|
let quote_token_index = 1;
|
||||||
|
let (_oracle1, _bank1) =
|
||||||
|
register_mint(quote_token_index, mint1.clone(), address_lookup_table).await;
|
||||||
|
|
||||||
|
//
|
||||||
|
// SETUP: Deposit user funds
|
||||||
|
//
|
||||||
|
{
|
||||||
|
let deposit_amount = 1000;
|
||||||
|
|
||||||
|
send_tx(
|
||||||
|
solana,
|
||||||
|
DepositInstruction {
|
||||||
|
amount: deposit_amount,
|
||||||
|
account,
|
||||||
|
token_account: payer_mint_accounts[0],
|
||||||
|
token_authority: payer,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
send_tx(
|
||||||
|
solana,
|
||||||
|
DepositInstruction {
|
||||||
|
amount: deposit_amount,
|
||||||
|
account,
|
||||||
|
token_account: payer_mint_accounts[1],
|
||||||
|
token_authority: payer,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// TEST: Create a perp market
|
||||||
|
//
|
||||||
|
let _perp_market = send_tx(
|
||||||
|
solana,
|
||||||
|
CreatePerpMarketInstruction {
|
||||||
|
group,
|
||||||
|
admin,
|
||||||
|
payer,
|
||||||
|
mint: mint0.pubkey,
|
||||||
|
// e.g. BTC mango-v3 mainnet.1
|
||||||
|
quote_lot_size: 10,
|
||||||
|
base_lot_size: 100,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.perp_market;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue