Merge branch 'dev'
This commit is contained in:
commit
31b71a285d
|
@ -12,7 +12,7 @@ url = "https://anchor.projectserum.com"
|
||||||
|
|
||||||
[provider]
|
[provider]
|
||||||
cluster = "localnet"
|
cluster = "localnet"
|
||||||
wallet = "/home/mc/.config/solana/id.json"
|
wallet = "~/.config/solana/id.json"
|
||||||
|
|
||||||
[scripts]
|
[scripts]
|
||||||
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 anchor-tests/*.ts"
|
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 anchor-tests/*.ts"
|
||||||
|
|
|
@ -2919,6 +2919,7 @@ dependencies = [
|
||||||
"fixed",
|
"fixed",
|
||||||
"fixed-macro",
|
"fixed-macro",
|
||||||
"futures 0.3.21",
|
"futures 0.3.21",
|
||||||
|
"itertools 0.10.3",
|
||||||
"log 0.4.17",
|
"log 0.4.17",
|
||||||
"mango-v4",
|
"mango-v4",
|
||||||
"pyth-sdk-solana",
|
"pyth-sdk-solana",
|
||||||
|
|
2
anchor
2
anchor
|
@ -1 +1 @@
|
||||||
Subproject commit 2058b6461cb0de5af90b04eb8fae4225a368251e
|
Subproject commit b52f23614601652a99ec6c27aec77bd327363b31
|
|
@ -283,4 +283,8 @@ describe('mango-v4', () => {
|
||||||
1000,
|
1000,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('update index and rate', async () => {
|
||||||
|
envClient.updateIndexAndRate(group, 'USDC');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,7 +15,7 @@ use bincode::Options;
|
||||||
use fixed::types::I80F48;
|
use fixed::types::I80F48;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use mango_v4::instructions::{Serum3OrderType, Serum3SelfTradeBehavior, Serum3Side};
|
use mango_v4::instructions::{Serum3OrderType, Serum3SelfTradeBehavior, Serum3Side};
|
||||||
use mango_v4::state::{AccountSize, Bank, Group, MangoAccountValue, Serum3MarketIndex, TokenIndex};
|
use mango_v4::state::{Bank, Group, MangoAccountValue, Serum3MarketIndex, TokenIndex};
|
||||||
|
|
||||||
use solana_client::nonblocking::rpc_client::RpcClient as RpcClientAsync;
|
use solana_client::nonblocking::rpc_client::RpcClient as RpcClientAsync;
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
|
@ -206,7 +206,10 @@ impl MangoClient {
|
||||||
data: anchor_lang::InstructionData::data(&mango_v4::instruction::AccountCreate {
|
data: anchor_lang::InstructionData::data(&mango_v4::instruction::AccountCreate {
|
||||||
account_num,
|
account_num,
|
||||||
name: mango_account_name.to_owned(),
|
name: mango_account_name.to_owned(),
|
||||||
account_size: AccountSize::Small,
|
token_count: 8,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.signer(owner)
|
.signer(owner)
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
RPC_URL=https://mango.devnet.rpcpool.com
|
|
||||||
PAYER_KEYPAIR=~/.config/solana/mango-devnet.json
|
|
||||||
GROUP_FROM_ADMIN_KEYPAIR=~/.config/solana/admin.json
|
|
||||||
MANGO_ACCOUNT_NAME=Account
|
|
|
@ -1,4 +0,0 @@
|
||||||
RPC_URL=https://mango.rpcpool.com/
|
|
||||||
PAYER_KEYPAIR=~/.config/solana/mango-mainnet.json
|
|
||||||
GROUP=grouppubkey
|
|
||||||
MANGO_ACCOUNT_NAME=Account
|
|
|
@ -17,6 +17,7 @@ env_logger = "0.8.4"
|
||||||
fixed = { version = "=1.11.0", features = ["serde", "borsh"] }
|
fixed = { version = "=1.11.0", features = ["serde", "borsh"] }
|
||||||
fixed-macro = "^1.1.1"
|
fixed-macro = "^1.1.1"
|
||||||
futures = "0.3.21"
|
futures = "0.3.21"
|
||||||
|
itertools = "0.10.3"
|
||||||
log = "0.4.0"
|
log = "0.4.0"
|
||||||
mango-v4 = { path = "../programs/mango-v4", features = ["no-entrypoint", "client"] }
|
mango-v4 = { path = "../programs/mango-v4", features = ["no-entrypoint", "client"] }
|
||||||
pyth-sdk-solana = "0.1.0"
|
pyth-sdk-solana = "0.1.0"
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
use crate::MangoClient;
|
use crate::MangoClient;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use anchor_lang::{__private::bytemuck::cast_ref, solana_program};
|
use anchor_lang::{__private::bytemuck::cast_ref, solana_program};
|
||||||
use client::prettify_client_error;
|
use client::prettify_client_error;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use mango_v4::state::{EventQueue, EventType, FillEvent, OutEvent, PerpMarket, TokenIndex};
|
use mango_v4::state::{EventQueue, EventType, FillEvent, OutEvent, PerpMarket, TokenIndex};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
|
compute_budget::ComputeBudgetInstruction,
|
||||||
instruction::{AccountMeta, Instruction},
|
instruction::{AccountMeta, Instruction},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
|
@ -22,7 +24,16 @@ pub async fn runner(
|
||||||
.context
|
.context
|
||||||
.tokens
|
.tokens
|
||||||
.keys()
|
.keys()
|
||||||
.map(|&token_index| loop_update_index_and_rate(mango_client.clone(), token_index))
|
// TokenUpdateIndexAndRate is known to take max 71k cu
|
||||||
|
// from cargo test-bpf local tests
|
||||||
|
.chunks(15)
|
||||||
|
.into_iter()
|
||||||
|
.map(|chunk| {
|
||||||
|
loop_update_index_and_rate(
|
||||||
|
mango_client.clone(),
|
||||||
|
chunk.copied().collect::<Vec<TokenIndex>>(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let handles2 = mango_client
|
let handles2 = mango_client
|
||||||
|
@ -49,27 +60,37 @@ pub async fn runner(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn loop_update_index_and_rate(mango_client: Arc<MangoClient>, token_index: TokenIndex) {
|
pub async fn loop_update_index_and_rate(
|
||||||
|
mango_client: Arc<MangoClient>,
|
||||||
|
token_indices: Vec<TokenIndex>,
|
||||||
|
) {
|
||||||
let mut interval = time::interval(Duration::from_secs(5));
|
let mut interval = time::interval(Duration::from_secs(5));
|
||||||
loop {
|
loop {
|
||||||
interval.tick().await;
|
interval.tick().await;
|
||||||
|
|
||||||
let client = mango_client.clone();
|
let client = mango_client.clone();
|
||||||
|
|
||||||
|
let token_indices_clone = token_indices.clone();
|
||||||
|
|
||||||
let res = tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
|
let res = tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
|
||||||
let token = client.context.token(token_index);
|
let token_names = token_indices_clone
|
||||||
|
.iter()
|
||||||
|
.map(|token_index| client.context.token(*token_index).name.to_owned())
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
let program = client.program();
|
||||||
|
let mut req = program.request();
|
||||||
|
req = req.instruction(ComputeBudgetInstruction::set_compute_unit_price(1));
|
||||||
|
for token_index in token_indices_clone.iter() {
|
||||||
|
let token = client.context.token(*token_index);
|
||||||
let banks_for_a_token = token.mint_info.banks();
|
let banks_for_a_token = token.mint_info.banks();
|
||||||
let token_name = &token.name;
|
|
||||||
let oracle = token.mint_info.oracle;
|
let oracle = token.mint_info.oracle;
|
||||||
|
|
||||||
let sig_result = client
|
|
||||||
.program()
|
|
||||||
.request()
|
|
||||||
.instruction({
|
|
||||||
let mut ix = Instruction {
|
let mut ix = Instruction {
|
||||||
program_id: mango_v4::id(),
|
program_id: mango_v4::id(),
|
||||||
accounts: anchor_lang::ToAccountMetas::to_account_metas(
|
accounts: anchor_lang::ToAccountMetas::to_account_metas(
|
||||||
&mango_v4::accounts::TokenUpdateIndexAndRate {
|
&mango_v4::accounts::TokenUpdateIndexAndRate {
|
||||||
|
group: token.mint_info.group,
|
||||||
mint_info: token.mint_info_address,
|
mint_info: token.mint_info_address,
|
||||||
oracle,
|
oracle,
|
||||||
instructions: solana_program::sysvar::instructions::id(),
|
instructions: solana_program::sysvar::instructions::id(),
|
||||||
|
@ -89,17 +110,16 @@ pub async fn loop_update_index_and_rate(mango_client: Arc<MangoClient>, token_in
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
ix.accounts.append(&mut banks);
|
ix.accounts.append(&mut banks);
|
||||||
ix
|
req = req.instruction(ix);
|
||||||
})
|
}
|
||||||
.send()
|
let sig_result = req.send().map_err(prettify_client_error);
|
||||||
.map_err(prettify_client_error);
|
|
||||||
|
|
||||||
if let Err(e) = sig_result {
|
if let Err(e) = sig_result {
|
||||||
log::error!("{:?}", e)
|
log::error!("{:?}", e)
|
||||||
} else {
|
} else {
|
||||||
log::info!(
|
log::info!(
|
||||||
"update_index_and_rate {} {:?}",
|
"update_index_and_rate {} {:?}",
|
||||||
token_name,
|
token_names,
|
||||||
sig_result.unwrap()
|
sig_result.unwrap()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -240,6 +260,7 @@ pub async fn loop_update_funding(
|
||||||
program_id: mango_v4::id(),
|
program_id: mango_v4::id(),
|
||||||
accounts: anchor_lang::ToAccountMetas::to_account_metas(
|
accounts: anchor_lang::ToAccountMetas::to_account_metas(
|
||||||
&mango_v4::accounts::PerpUpdateFunding {
|
&mango_v4::accounts::PerpUpdateFunding {
|
||||||
|
group: perp_market.group,
|
||||||
perp_market: pk,
|
perp_market: pk,
|
||||||
asks: perp_market.asks,
|
asks: perp_market.asks,
|
||||||
bids: perp_market.bids,
|
bids: perp_market.bids,
|
||||||
|
|
|
@ -76,7 +76,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mango_client = Arc::new(MangoClient::new_for_existing_account(
|
let mango_client = Arc::new(MangoClient::new_for_existing_account(
|
||||||
Client::new(cluster, commitment, &owner, Some(Duration::from_secs(1))),
|
Client::new(cluster, commitment, &owner, Some(Duration::from_secs(10))),
|
||||||
cli.mango_account,
|
cli.mango_account,
|
||||||
owner,
|
owner,
|
||||||
)?);
|
)?);
|
||||||
|
|
|
@ -96,7 +96,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
let rpc_url = cli.rpc_url;
|
let rpc_url = cli.rpc_url;
|
||||||
let ws_url = rpc_url.replace("https", "wss");
|
let ws_url = rpc_url.replace("https", "wss");
|
||||||
|
|
||||||
let rpc_timeout = Duration::from_secs(1);
|
let rpc_timeout = Duration::from_secs(10);
|
||||||
let cluster = Cluster::Custom(rpc_url.clone(), ws_url.clone());
|
let cluster = Cluster::Custom(rpc_url.clone(), ws_url.clone());
|
||||||
let commitment = CommitmentConfig::processed();
|
let commitment = CommitmentConfig::processed();
|
||||||
let client = Client::new(cluster.clone(), commitment, &liqor_owner, Some(rpc_timeout));
|
let client = Client::new(cluster.clone(), commitment, &liqor_owner, Some(rpc_timeout));
|
||||||
|
|
|
@ -8,7 +8,12 @@ use crate::state::*;
|
||||||
pub struct AccountClose<'info> {
|
pub struct AccountClose<'info> {
|
||||||
pub group: AccountLoader<'info, Group>,
|
pub group: AccountLoader<'info, Group>,
|
||||||
|
|
||||||
#[account(mut, has_one = group, has_one = owner, close = sol_destination)]
|
#[account(
|
||||||
|
mut,
|
||||||
|
has_one = group,
|
||||||
|
has_one = owner,
|
||||||
|
close = sol_destination
|
||||||
|
)]
|
||||||
pub account: AccountLoaderDynamic<'info, MangoAccount>,
|
pub account: AccountLoaderDynamic<'info, MangoAccount>,
|
||||||
pub owner: Signer<'info>,
|
pub owner: Signer<'info>,
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::state::*;
|
||||||
use crate::util::fill32_from_str;
|
use crate::util::fill32_from_str;
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
#[instruction(account_num: u32, account_size: AccountSize)]
|
#[instruction(account_num: u32, token_count: u8, serum3_count: u8, perp_count: u8, perp_oo_count: u8)]
|
||||||
pub struct AccountCreate<'info> {
|
pub struct AccountCreate<'info> {
|
||||||
pub group: AccountLoader<'info, Group>,
|
pub group: AccountLoader<'info, Group>,
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ pub struct AccountCreate<'info> {
|
||||||
seeds = [group.key().as_ref(), b"MangoAccount".as_ref(), owner.key().as_ref(), &account_num.to_le_bytes()],
|
seeds = [group.key().as_ref(), b"MangoAccount".as_ref(), owner.key().as_ref(), &account_num.to_le_bytes()],
|
||||||
bump,
|
bump,
|
||||||
payer = payer,
|
payer = payer,
|
||||||
space = MangoAccount::space(account_size),
|
space = MangoAccount::space(token_count, serum3_count, perp_count, perp_oo_count)?,
|
||||||
)]
|
)]
|
||||||
pub account: AccountLoaderDynamic<'info, MangoAccount>,
|
pub account: AccountLoaderDynamic<'info, MangoAccount>,
|
||||||
pub owner: Signer<'info>,
|
pub owner: Signer<'info>,
|
||||||
|
@ -28,7 +28,10 @@ pub struct AccountCreate<'info> {
|
||||||
pub fn account_create(
|
pub fn account_create(
|
||||||
ctx: Context<AccountCreate>,
|
ctx: Context<AccountCreate>,
|
||||||
account_num: u32,
|
account_num: u32,
|
||||||
account_size: AccountSize,
|
token_count: u8,
|
||||||
|
serum3_count: u8,
|
||||||
|
perp_count: u8,
|
||||||
|
perp_oo_count: u8,
|
||||||
name: String,
|
name: String,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut account = ctx.accounts.account.load_init()?;
|
let mut account = ctx.accounts.account.load_init()?;
|
||||||
|
@ -47,7 +50,7 @@ pub fn account_create(
|
||||||
account.fixed.set_being_liquidated(false);
|
account.fixed.set_being_liquidated(false);
|
||||||
account.fixed.set_bankrupt(false);
|
account.fixed.set_bankrupt(false);
|
||||||
|
|
||||||
account.expand_dynamic_content(account_size)?;
|
account.expand_dynamic_content(token_count, serum3_count, perp_count, perp_oo_count)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,15 +20,14 @@ pub struct AccountExpand<'info> {
|
||||||
pub system_program: Program<'info, System>,
|
pub system_program: Program<'info, System>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn account_expand(ctx: Context<AccountExpand>) -> Result<()> {
|
pub fn account_expand(
|
||||||
let account_size = {
|
ctx: Context<AccountExpand>,
|
||||||
let account = ctx.accounts.account.load()?;
|
token_count: u8,
|
||||||
account.size()
|
serum3_count: u8,
|
||||||
};
|
perp_count: u8,
|
||||||
|
perp_oo_count: u8,
|
||||||
require_eq!(account_size, AccountSize::Small);
|
) -> Result<()> {
|
||||||
|
let new_space = MangoAccount::space(token_count, serum3_count, perp_count, perp_oo_count)?;
|
||||||
let new_space = MangoAccount::space(AccountSize::Large);
|
|
||||||
let new_rent_minimum = Rent::get()?.minimum_balance(new_space);
|
let new_rent_minimum = Rent::get()?.minimum_balance(new_space);
|
||||||
|
|
||||||
let realloc_account = ctx.accounts.account.as_ref();
|
let realloc_account = ctx.accounts.account.as_ref();
|
||||||
|
@ -55,7 +54,7 @@ pub fn account_expand(ctx: Context<AccountExpand>) -> Result<()> {
|
||||||
|
|
||||||
// expand dynamic content, e.g. to grow token positions, we need to slide serum3orders further later, and so on....
|
// expand dynamic content, e.g. to grow token positions, we need to slide serum3orders further later, and so on....
|
||||||
let mut account = ctx.accounts.account.load_mut()?;
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
account.expand_dynamic_content(AccountSize::Large)?;
|
account.expand_dynamic_content(token_count, serum3_count, perp_count, perp_oo_count)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::state::{
|
||||||
use crate::util::checked_math as cm;
|
use crate::util::checked_math as cm;
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
|
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
|
||||||
|
use anchor_lang::Discriminator;
|
||||||
use anchor_spl::token::{self, Token, TokenAccount};
|
use anchor_spl::token::{self, Token, TokenAccount};
|
||||||
use fixed::types::I80F48;
|
use fixed::types::I80F48;
|
||||||
|
|
||||||
|
@ -76,19 +77,20 @@ pub fn flash_loan_begin<'key, 'accounts, 'remaining, 'info>(
|
||||||
require_keys_eq!(bank.group, ctx.accounts.group.key());
|
require_keys_eq!(bank.group, ctx.accounts.group.key());
|
||||||
require_keys_eq!(bank.vault, *vault_ai.key);
|
require_keys_eq!(bank.vault, *vault_ai.key);
|
||||||
|
|
||||||
|
let vault = Account::<TokenAccount>::try_from(vault_ai)?;
|
||||||
let token_account = Account::<TokenAccount>::try_from(token_account_ai)?;
|
let token_account = Account::<TokenAccount>::try_from(token_account_ai)?;
|
||||||
|
|
||||||
bank.flash_loan_approved_amount = *amount;
|
bank.flash_loan_approved_amount = *amount;
|
||||||
bank.flash_loan_vault_initial = token_account.amount;
|
bank.flash_loan_token_account_initial = token_account.amount;
|
||||||
|
|
||||||
// Transfer the loaned funds
|
// Transfer the loaned funds
|
||||||
if *amount > 0 {
|
if *amount > 0 {
|
||||||
// Provide a readable error message in case the vault doesn't have enough tokens
|
// Provide a readable error message in case the vault doesn't have enough tokens
|
||||||
if token_account.amount < *amount {
|
if vault.amount < *amount {
|
||||||
return err!(MangoError::InsufficentBankVaultFunds).with_context(|| {
|
return err!(MangoError::InsufficentBankVaultFunds).with_context(|| {
|
||||||
format!(
|
format!(
|
||||||
"bank vault {} does not have enough tokens, need {} but have {}",
|
"bank vault {} does not have enough tokens, need {} but have {}",
|
||||||
vault_ai.key, amount, token_account.amount
|
vault_ai.key, amount, vault.amount
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -141,11 +143,11 @@ pub fn flash_loan_begin<'key, 'accounts, 'remaining, 'info>(
|
||||||
|
|
||||||
// must be the FlashLoanEnd instruction
|
// must be the FlashLoanEnd instruction
|
||||||
require!(
|
require!(
|
||||||
ix.data[0..8] == [178, 170, 2, 78, 240, 23, 190, 178],
|
ix.data[0..8] == crate::instruction::FlashLoanEnd::discriminator(),
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
// check that the same vaults are passed
|
// check that the same vaults and token accounts are passed
|
||||||
let begin_accounts = &ctx.remaining_accounts[num_loans..];
|
let begin_accounts = &ctx.remaining_accounts[num_loans..];
|
||||||
let end_accounts = &ix.accounts[ix.accounts.len() - 2 * num_loans..];
|
let end_accounts = &ix.accounts[ix.accounts.len() - 2 * num_loans..];
|
||||||
for (begin_account, end_account) in begin_accounts.iter().zip(end_accounts.iter()) {
|
for (begin_account, end_account) in begin_accounts.iter().zip(end_accounts.iter()) {
|
||||||
|
@ -180,6 +182,7 @@ pub fn flash_loan_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
ctx: Context<'key, 'accounts, 'remaining, 'info, FlashLoanEnd<'info>>,
|
ctx: Context<'key, 'accounts, 'remaining, 'info, FlashLoanEnd<'info>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut account = ctx.accounts.account.load_mut()?;
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
|
let group = account.fixed.group;
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
||||||
|
|
||||||
|
@ -233,7 +236,7 @@ pub fn flash_loan_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
// The Begin instruction only checks that End ends with the same vault accounts -
|
// The Begin instruction only checks that End ends with the same vault accounts -
|
||||||
// but there could be an extra vault account in End, or a different bank could be
|
// but there could be an extra vault account in End, or a different bank could be
|
||||||
// used for the same vault.
|
// used for the same vault.
|
||||||
require_neq!(bank.flash_loan_vault_initial, u64::MAX);
|
require_neq!(bank.flash_loan_token_account_initial, u64::MAX);
|
||||||
|
|
||||||
// Create the token position now, so we can compute the pre-health with fixed order health accounts
|
// Create the token position now, so we can compute the pre-health with fixed order health accounts
|
||||||
let (_, raw_token_index, _) = account.token_get_mut_or_create(bank.token_index)?;
|
let (_, raw_token_index, _) = account.token_get_mut_or_create(bank.token_index)?;
|
||||||
|
@ -241,7 +244,7 @@ pub fn flash_loan_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
// Transfer any excess over the inital balance of the token account back
|
// Transfer any excess over the inital balance of the token account back
|
||||||
// into the vault. Compute the total change in the vault balance.
|
// into the vault. Compute the total change in the vault balance.
|
||||||
let mut change = -I80F48::from(bank.flash_loan_approved_amount);
|
let mut change = -I80F48::from(bank.flash_loan_approved_amount);
|
||||||
if token_account.amount > bank.flash_loan_vault_initial {
|
if token_account.amount > bank.flash_loan_token_account_initial {
|
||||||
let transfer_ctx = CpiContext::new(
|
let transfer_ctx = CpiContext::new(
|
||||||
ctx.accounts.token_program.to_account_info(),
|
ctx.accounts.token_program.to_account_info(),
|
||||||
token::Transfer {
|
token::Transfer {
|
||||||
|
@ -250,7 +253,7 @@ pub fn flash_loan_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
authority: ctx.accounts.owner.to_account_info(),
|
authority: ctx.accounts.owner.to_account_info(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let repay = token_account.amount - bank.flash_loan_vault_initial;
|
let repay = token_account.amount - bank.flash_loan_token_account_initial;
|
||||||
token::transfer(transfer_ctx, repay)?;
|
token::transfer(transfer_ctx, repay)?;
|
||||||
|
|
||||||
let repay = I80F48::from(repay);
|
let repay = I80F48::from(repay);
|
||||||
|
@ -322,7 +325,7 @@ pub fn flash_loan_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
}
|
}
|
||||||
|
|
||||||
bank.flash_loan_approved_amount = 0;
|
bank.flash_loan_approved_amount = 0;
|
||||||
bank.flash_loan_vault_initial = u64::MAX;
|
bank.flash_loan_token_account_initial = u64::MAX;
|
||||||
|
|
||||||
token_loan_details.push(FlashLoanTokenDetail {
|
token_loan_details.push(FlashLoanTokenDetail {
|
||||||
token_index: position.token_index,
|
token_index: position.token_index,
|
||||||
|
@ -335,6 +338,7 @@ pub fn flash_loan_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
});
|
});
|
||||||
|
|
||||||
emit!(TokenBalanceLog {
|
emit!(TokenBalanceLog {
|
||||||
|
mango_group: group.key(),
|
||||||
mango_account: ctx.accounts.account.key(),
|
mango_account: ctx.accounts.account.key(),
|
||||||
token_index: bank.token_index as u16,
|
token_index: bank.token_index as u16,
|
||||||
indexed_position: position.indexed_position.to_bits(),
|
indexed_position: position.indexed_position.to_bits(),
|
||||||
|
@ -345,6 +349,7 @@ pub fn flash_loan_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
}
|
}
|
||||||
|
|
||||||
emit!(FlashLoanLog {
|
emit!(FlashLoanLog {
|
||||||
|
mango_group: group.key(),
|
||||||
mango_account: ctx.accounts.account.key(),
|
mango_account: ctx.accounts.account.key(),
|
||||||
token_loan_details
|
token_loan_details
|
||||||
});
|
});
|
||||||
|
|
|
@ -157,6 +157,7 @@ pub fn liq_token_with_token(
|
||||||
);
|
);
|
||||||
|
|
||||||
emit!(LiquidateTokenAndTokenLog {
|
emit!(LiquidateTokenAndTokenLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
liqee: ctx.accounts.liqee.key(),
|
liqee: ctx.accounts.liqee.key(),
|
||||||
liqor: ctx.accounts.liqor.key(),
|
liqor: ctx.accounts.liqor.key(),
|
||||||
asset_token_index,
|
asset_token_index,
|
||||||
|
@ -170,6 +171,7 @@ pub fn liq_token_with_token(
|
||||||
|
|
||||||
// liqee asset
|
// liqee asset
|
||||||
emit!(TokenBalanceLog {
|
emit!(TokenBalanceLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
mango_account: ctx.accounts.liqee.key(),
|
mango_account: ctx.accounts.liqee.key(),
|
||||||
token_index: asset_token_index,
|
token_index: asset_token_index,
|
||||||
indexed_position: liqee_asset_position_indexed.to_bits(),
|
indexed_position: liqee_asset_position_indexed.to_bits(),
|
||||||
|
@ -179,6 +181,7 @@ pub fn liq_token_with_token(
|
||||||
});
|
});
|
||||||
// liqee liab
|
// liqee liab
|
||||||
emit!(TokenBalanceLog {
|
emit!(TokenBalanceLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
mango_account: ctx.accounts.liqee.key(),
|
mango_account: ctx.accounts.liqee.key(),
|
||||||
token_index: liab_token_index,
|
token_index: liab_token_index,
|
||||||
indexed_position: liqee_liab_position_indexed.to_bits(),
|
indexed_position: liqee_liab_position_indexed.to_bits(),
|
||||||
|
@ -188,6 +191,7 @@ pub fn liq_token_with_token(
|
||||||
});
|
});
|
||||||
// liqor asset
|
// liqor asset
|
||||||
emit!(TokenBalanceLog {
|
emit!(TokenBalanceLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
mango_account: ctx.accounts.liqor.key(),
|
mango_account: ctx.accounts.liqor.key(),
|
||||||
token_index: asset_token_index,
|
token_index: asset_token_index,
|
||||||
indexed_position: liqor_asset_position_indexed.to_bits(),
|
indexed_position: liqor_asset_position_indexed.to_bits(),
|
||||||
|
@ -197,6 +201,7 @@ pub fn liq_token_with_token(
|
||||||
});
|
});
|
||||||
// liqor liab
|
// liqor liab
|
||||||
emit!(TokenBalanceLog {
|
emit!(TokenBalanceLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
mango_account: ctx.accounts.liqor.key(),
|
mango_account: ctx.accounts.liqor.key(),
|
||||||
token_index: liab_token_index,
|
token_index: liab_token_index,
|
||||||
indexed_position: liqor_liab_position_indexed.to_bits(),
|
indexed_position: liqor_liab_position_indexed.to_bits(),
|
||||||
|
|
|
@ -61,6 +61,7 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
|
||||||
fill,
|
fill,
|
||||||
)?;
|
)?;
|
||||||
emit_perp_balances(
|
emit_perp_balances(
|
||||||
|
ctx.accounts.group.key(),
|
||||||
fill.maker,
|
fill.maker,
|
||||||
perp_market.perp_market_index as u64,
|
perp_market.perp_market_index as u64,
|
||||||
fill.price,
|
fill.price,
|
||||||
|
@ -101,6 +102,7 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
|
||||||
fill,
|
fill,
|
||||||
)?;
|
)?;
|
||||||
emit_perp_balances(
|
emit_perp_balances(
|
||||||
|
ctx.accounts.group.key(),
|
||||||
fill.maker,
|
fill.maker,
|
||||||
perp_market.perp_market_index as u64,
|
perp_market.perp_market_index as u64,
|
||||||
fill.price,
|
fill.price,
|
||||||
|
@ -110,6 +112,7 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
|
||||||
&perp_market,
|
&perp_market,
|
||||||
);
|
);
|
||||||
emit_perp_balances(
|
emit_perp_balances(
|
||||||
|
ctx.accounts.group.key(),
|
||||||
fill.taker,
|
fill.taker,
|
||||||
perp_market.perp_market_index as u64,
|
perp_market.perp_market_index as u64,
|
||||||
fill.price,
|
fill.price,
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
use crate::accounts_zerocopy::*;
|
use crate::accounts_zerocopy::*;
|
||||||
use crate::state::{oracle_price, Book, BookSide, PerpMarket};
|
use crate::state::{oracle_price, Book, BookSide, Group, PerpMarket};
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct PerpUpdateFunding<'info> {
|
pub struct PerpUpdateFunding<'info> {
|
||||||
|
pub group: AccountLoader<'info, Group>, // Required for group metadata parsing
|
||||||
|
|
||||||
#[account(
|
#[account(
|
||||||
mut,
|
mut,
|
||||||
has_one = bids,
|
has_one = bids,
|
||||||
has_one = asks,
|
has_one = asks,
|
||||||
has_one = oracle,
|
has_one = oracle,
|
||||||
|
constraint = perp_market.load()?.group.key() == group.key(),
|
||||||
)]
|
)]
|
||||||
pub perp_market: AccountLoader<'info, PerpMarket>,
|
pub perp_market: AccountLoader<'info, PerpMarket>,
|
||||||
#[account(mut)]
|
#[account(mut)]
|
||||||
|
|
|
@ -83,6 +83,7 @@ pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
|
||||||
cm!(amount_i80f48 * oracle_price * QUOTE_NATIVE_TO_UI).to_num::<f32>();
|
cm!(amount_i80f48 * oracle_price * QUOTE_NATIVE_TO_UI).to_num::<f32>();
|
||||||
|
|
||||||
emit!(TokenBalanceLog {
|
emit!(TokenBalanceLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
mango_account: ctx.accounts.account.key(),
|
mango_account: ctx.accounts.account.key(),
|
||||||
token_index,
|
token_index,
|
||||||
indexed_position: indexed_position.to_bits(),
|
indexed_position: indexed_position.to_bits(),
|
||||||
|
@ -111,6 +112,7 @@ pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
emit!(DepositLog {
|
emit!(DepositLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
mango_account: ctx.accounts.account.key(),
|
mango_account: ctx.accounts.account.key(),
|
||||||
signer: ctx.accounts.token_authority.key(),
|
signer: ctx.accounts.token_authority.key(),
|
||||||
token_index,
|
token_index,
|
||||||
|
|
|
@ -30,6 +30,7 @@ pub fn token_edit(
|
||||||
bank_num: u64,
|
bank_num: u64,
|
||||||
oracle_opt: Option<Pubkey>,
|
oracle_opt: Option<Pubkey>,
|
||||||
oracle_config_opt: Option<OracleConfig>,
|
oracle_config_opt: Option<OracleConfig>,
|
||||||
|
group_insurance_fund_opt: Option<bool>,
|
||||||
interest_rate_params_opt: Option<InterestRateParams>,
|
interest_rate_params_opt: Option<InterestRateParams>,
|
||||||
loan_fee_rate_opt: Option<f32>,
|
loan_fee_rate_opt: Option<f32>,
|
||||||
loan_origination_fee_rate_opt: Option<f32>,
|
loan_origination_fee_rate_opt: Option<f32>,
|
||||||
|
@ -62,6 +63,10 @@ pub fn token_edit(
|
||||||
bank.oracle_config = oracle_config;
|
bank.oracle_config = oracle_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(group_insurance_fund) = group_insurance_fund_opt {
|
||||||
|
mint_info.group_insurance_fund = if group_insurance_fund { 1 } else { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
// unchanged -
|
// unchanged -
|
||||||
// deposit_index
|
// deposit_index
|
||||||
// borrow_index
|
// borrow_index
|
||||||
|
@ -109,7 +114,7 @@ pub fn token_edit(
|
||||||
|
|
||||||
// unchanged -
|
// unchanged -
|
||||||
// dust
|
// dust
|
||||||
// flash_loan_vault_initial
|
// flash_loan_token_account_initial
|
||||||
// flash_loan_approved_amount
|
// flash_loan_approved_amount
|
||||||
// token_index
|
// token_index
|
||||||
// bump
|
// bump
|
||||||
|
|
|
@ -131,13 +131,13 @@ pub fn token_register(
|
||||||
init_liab_weight: I80F48::from_num(init_liab_weight),
|
init_liab_weight: I80F48::from_num(init_liab_weight),
|
||||||
liquidation_fee: I80F48::from_num(liquidation_fee),
|
liquidation_fee: I80F48::from_num(liquidation_fee),
|
||||||
dust: I80F48::ZERO,
|
dust: I80F48::ZERO,
|
||||||
flash_loan_vault_initial: u64::MAX,
|
flash_loan_token_account_initial: u64::MAX,
|
||||||
flash_loan_approved_amount: 0,
|
flash_loan_approved_amount: 0,
|
||||||
token_index,
|
token_index,
|
||||||
bump: *ctx.bumps.get("bank").ok_or(MangoError::SomeError)?,
|
bump: *ctx.bumps.get("bank").ok_or(MangoError::SomeError)?,
|
||||||
mint_decimals: ctx.accounts.mint.decimals,
|
mint_decimals: ctx.accounts.mint.decimals,
|
||||||
bank_num: 0,
|
bank_num: 0,
|
||||||
reserved: [0; 256],
|
reserved: [0; 2560],
|
||||||
};
|
};
|
||||||
require_gt!(bank.max_rate, MINIMUM_MAX_RATE);
|
require_gt!(bank.max_rate, MINIMUM_MAX_RATE);
|
||||||
|
|
||||||
|
@ -145,14 +145,14 @@ pub fn token_register(
|
||||||
*mint_info = MintInfo {
|
*mint_info = MintInfo {
|
||||||
group: ctx.accounts.group.key(),
|
group: ctx.accounts.group.key(),
|
||||||
token_index,
|
token_index,
|
||||||
|
group_insurance_fund: 1,
|
||||||
padding1: Default::default(),
|
padding1: Default::default(),
|
||||||
mint: ctx.accounts.mint.key(),
|
mint: ctx.accounts.mint.key(),
|
||||||
banks: Default::default(),
|
banks: Default::default(),
|
||||||
vaults: Default::default(),
|
vaults: Default::default(),
|
||||||
oracle: ctx.accounts.oracle.key(),
|
oracle: ctx.accounts.oracle.key(),
|
||||||
registration_time: Clock::get()?.unix_timestamp,
|
registration_time: Clock::get()?.unix_timestamp,
|
||||||
group_insurance_fund: 1,
|
reserved: [0; 2560],
|
||||||
reserved: [0; 255],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mint_info.banks[0] = ctx.accounts.bank.key();
|
mint_info.banks[0] = ctx.accounts.bank.key();
|
||||||
|
|
|
@ -88,28 +88,29 @@ pub fn token_register_trustless(
|
||||||
index_last_updated: Clock::get()?.unix_timestamp,
|
index_last_updated: Clock::get()?.unix_timestamp,
|
||||||
bank_rate_last_updated: Clock::get()?.unix_timestamp,
|
bank_rate_last_updated: Clock::get()?.unix_timestamp,
|
||||||
avg_utilization: I80F48::ZERO,
|
avg_utilization: I80F48::ZERO,
|
||||||
adjustment_factor: I80F48::from_num(0.02),
|
// 10% daily adjustment at 0% or 100% utilization
|
||||||
|
adjustment_factor: I80F48::from_num(0.004),
|
||||||
util0: I80F48::from_num(0.7),
|
util0: I80F48::from_num(0.7),
|
||||||
rate0: I80F48::from_num(0.1),
|
rate0: I80F48::from_num(0.1),
|
||||||
util1: I80F48::from_num(0.8),
|
util1: I80F48::from_num(0.85),
|
||||||
rate1: I80F48::from_num(0.2),
|
rate1: I80F48::from_num(0.2),
|
||||||
max_rate: I80F48::from_num(2.0),
|
max_rate: I80F48::from_num(2.0),
|
||||||
collected_fees_native: I80F48::ZERO,
|
collected_fees_native: I80F48::ZERO,
|
||||||
loan_origination_fee_rate: I80F48::from_num(0.001),
|
loan_origination_fee_rate: I80F48::from_num(0.0005),
|
||||||
loan_fee_rate: I80F48::from_num(0.005),
|
loan_fee_rate: I80F48::from_num(0.005),
|
||||||
maint_asset_weight: I80F48::from_num(0),
|
maint_asset_weight: I80F48::from_num(0),
|
||||||
init_asset_weight: I80F48::from_num(0),
|
init_asset_weight: I80F48::from_num(0),
|
||||||
maint_liab_weight: I80F48::from_num(1.25),
|
maint_liab_weight: I80F48::from_num(1.4), // 2.5x
|
||||||
init_liab_weight: I80F48::from_num(1.5),
|
init_liab_weight: I80F48::from_num(1.8), // 1.25x
|
||||||
liquidation_fee: I80F48::from_num(0.125),
|
liquidation_fee: I80F48::from_num(0.2),
|
||||||
dust: I80F48::ZERO,
|
dust: I80F48::ZERO,
|
||||||
flash_loan_vault_initial: u64::MAX,
|
flash_loan_token_account_initial: u64::MAX,
|
||||||
flash_loan_approved_amount: 0,
|
flash_loan_approved_amount: 0,
|
||||||
token_index,
|
token_index,
|
||||||
bump: *ctx.bumps.get("bank").ok_or(MangoError::SomeError)?,
|
bump: *ctx.bumps.get("bank").ok_or(MangoError::SomeError)?,
|
||||||
mint_decimals: ctx.accounts.mint.decimals,
|
mint_decimals: ctx.accounts.mint.decimals,
|
||||||
bank_num: 0,
|
bank_num: 0,
|
||||||
reserved: [0; 256],
|
reserved: [0; 2560],
|
||||||
};
|
};
|
||||||
require_gt!(bank.max_rate, MINIMUM_MAX_RATE);
|
require_gt!(bank.max_rate, MINIMUM_MAX_RATE);
|
||||||
|
|
||||||
|
@ -117,14 +118,14 @@ pub fn token_register_trustless(
|
||||||
*mint_info = MintInfo {
|
*mint_info = MintInfo {
|
||||||
group: ctx.accounts.group.key(),
|
group: ctx.accounts.group.key(),
|
||||||
token_index,
|
token_index,
|
||||||
|
group_insurance_fund: 0,
|
||||||
padding1: Default::default(),
|
padding1: Default::default(),
|
||||||
mint: ctx.accounts.mint.key(),
|
mint: ctx.accounts.mint.key(),
|
||||||
banks: Default::default(),
|
banks: Default::default(),
|
||||||
vaults: Default::default(),
|
vaults: Default::default(),
|
||||||
oracle: ctx.accounts.oracle.key(),
|
oracle: ctx.accounts.oracle.key(),
|
||||||
registration_time: Clock::get()?.unix_timestamp,
|
registration_time: Clock::get()?.unix_timestamp,
|
||||||
group_insurance_fund: 0,
|
reserved: [0; 2560],
|
||||||
reserved: [0; 255],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mint_info.banks[0] = ctx.accounts.bank.key();
|
mint_info.banks[0] = ctx.accounts.bank.key();
|
||||||
|
|
|
@ -5,16 +5,25 @@ use crate::logs::{UpdateIndexLog, UpdateRateLog};
|
||||||
use crate::state::HOUR;
|
use crate::state::HOUR;
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts_zerocopy::{AccountInfoRef, LoadMutZeroCopyRef, LoadZeroCopyRef},
|
accounts_zerocopy::{AccountInfoRef, LoadMutZeroCopyRef, LoadZeroCopyRef},
|
||||||
state::{oracle_price, Bank, MintInfo},
|
state::{oracle_price, Bank, Group, MintInfo},
|
||||||
};
|
};
|
||||||
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
|
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
|
||||||
|
use anchor_lang::Discriminator;
|
||||||
use checked_math as cm;
|
use checked_math as cm;
|
||||||
use fixed::types::I80F48;
|
use fixed::types::I80F48;
|
||||||
|
|
||||||
|
pub mod compute_budget {
|
||||||
|
use solana_program::declare_id;
|
||||||
|
declare_id!("ComputeBudget111111111111111111111111111111");
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct TokenUpdateIndexAndRate<'info> {
|
pub struct TokenUpdateIndexAndRate<'info> {
|
||||||
|
pub group: AccountLoader<'info, Group>, // Required for group metadata parsing
|
||||||
|
|
||||||
#[account(
|
#[account(
|
||||||
has_one = oracle
|
has_one = oracle,
|
||||||
|
constraint = mint_info.load()?.group.key() == group.key(),
|
||||||
)]
|
)]
|
||||||
pub mint_info: AccountLoader<'info, MintInfo>,
|
pub mint_info: AccountLoader<'info, MintInfo>,
|
||||||
|
|
||||||
|
@ -42,8 +51,10 @@ pub fn token_update_index_and_rate(ctx: Context<TokenUpdateIndexAndRate>) -> Res
|
||||||
// for now we just whitelist to other token_update_index_and_rate ix
|
// for now we just whitelist to other token_update_index_and_rate ix
|
||||||
// 2. we want to forbid cpi, since ix we would like to blacklist could just be called from cpi
|
// 2. we want to forbid cpi, since ix we would like to blacklist could just be called from cpi
|
||||||
require!(
|
require!(
|
||||||
ix.program_id == crate::id()
|
(ix.program_id == crate::id()
|
||||||
&& ix.data[0..8] == [131, 136, 194, 39, 11, 50, 10, 198], // token_update_index_and_rate
|
&& ix.data[0..8]
|
||||||
|
== crate::instruction::TokenUpdateIndexAndRate::discriminator())
|
||||||
|
|| (ix.program_id == compute_budget::id()),
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,7 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
||||||
cm!(amount_i80f48 * oracle_price * QUOTE_NATIVE_TO_UI).to_num::<f32>();
|
cm!(amount_i80f48 * oracle_price * QUOTE_NATIVE_TO_UI).to_num::<f32>();
|
||||||
|
|
||||||
emit!(TokenBalanceLog {
|
emit!(TokenBalanceLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
mango_account: ctx.accounts.account.key(),
|
mango_account: ctx.accounts.account.key(),
|
||||||
token_index,
|
token_index,
|
||||||
indexed_position: indexed_position.to_bits(),
|
indexed_position: indexed_position.to_bits(),
|
||||||
|
@ -151,6 +152,7 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
||||||
}
|
}
|
||||||
|
|
||||||
emit!(WithdrawLog {
|
emit!(WithdrawLog {
|
||||||
|
mango_group: ctx.accounts.group.key(),
|
||||||
mango_account: ctx.accounts.account.key(),
|
mango_account: ctx.accounts.account.key(),
|
||||||
signer: ctx.accounts.owner.key(),
|
signer: ctx.accounts.owner.key(),
|
||||||
token_index,
|
token_index,
|
||||||
|
|
|
@ -19,16 +19,14 @@ pub mod serum3_cpi;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
use state::{
|
use state::{OracleConfig, OrderType, PerpMarketIndex, Serum3MarketIndex, Side, TokenIndex};
|
||||||
AccountSize, OracleConfig, OrderType, PerpMarketIndex, Serum3MarketIndex, Side, TokenIndex,
|
|
||||||
};
|
|
||||||
|
|
||||||
declare_id!("m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD");
|
declare_id!("m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD");
|
||||||
|
|
||||||
#[program]
|
#[program]
|
||||||
pub mod mango_v4 {
|
pub mod mango_v4 {
|
||||||
|
|
||||||
use crate::state::{AccountSize, OracleConfig};
|
use crate::state::OracleConfig;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -98,6 +96,7 @@ pub mod mango_v4 {
|
||||||
bank_num: u64,
|
bank_num: u64,
|
||||||
oracle_opt: Option<Pubkey>,
|
oracle_opt: Option<Pubkey>,
|
||||||
oracle_config_opt: Option<OracleConfig>,
|
oracle_config_opt: Option<OracleConfig>,
|
||||||
|
group_insurance_fund_opt: Option<bool>,
|
||||||
interest_rate_params_opt: Option<InterestRateParams>,
|
interest_rate_params_opt: Option<InterestRateParams>,
|
||||||
loan_fee_rate_opt: Option<f32>,
|
loan_fee_rate_opt: Option<f32>,
|
||||||
loan_origination_fee_rate_opt: Option<f32>,
|
loan_origination_fee_rate_opt: Option<f32>,
|
||||||
|
@ -112,6 +111,7 @@ pub mod mango_v4 {
|
||||||
bank_num,
|
bank_num,
|
||||||
oracle_opt,
|
oracle_opt,
|
||||||
oracle_config_opt,
|
oracle_config_opt,
|
||||||
|
group_insurance_fund_opt,
|
||||||
interest_rate_params_opt,
|
interest_rate_params_opt,
|
||||||
loan_fee_rate_opt,
|
loan_fee_rate_opt,
|
||||||
loan_origination_fee_rate_opt,
|
loan_origination_fee_rate_opt,
|
||||||
|
@ -146,14 +146,31 @@ pub mod mango_v4 {
|
||||||
pub fn account_create(
|
pub fn account_create(
|
||||||
ctx: Context<AccountCreate>,
|
ctx: Context<AccountCreate>,
|
||||||
account_num: u32,
|
account_num: u32,
|
||||||
account_size: AccountSize,
|
token_count: u8,
|
||||||
|
serum3_count: u8,
|
||||||
|
perp_count: u8,
|
||||||
|
perp_oo_count: u8,
|
||||||
name: String,
|
name: String,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
instructions::account_create(ctx, account_num, account_size, name)
|
instructions::account_create(
|
||||||
|
ctx,
|
||||||
|
account_num,
|
||||||
|
token_count,
|
||||||
|
serum3_count,
|
||||||
|
perp_count,
|
||||||
|
perp_oo_count,
|
||||||
|
name,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn account_expand(ctx: Context<AccountExpand>) -> Result<()> {
|
pub fn account_expand(
|
||||||
instructions::account_expand(ctx)
|
ctx: Context<AccountExpand>,
|
||||||
|
token_count: u8,
|
||||||
|
serum3_count: u8,
|
||||||
|
perp_count: u8,
|
||||||
|
perp_oo_count: u8,
|
||||||
|
) -> Result<()> {
|
||||||
|
instructions::account_expand(ctx, token_count, serum3_count, perp_count, perp_oo_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn account_edit(
|
pub fn account_edit(
|
||||||
|
|
|
@ -4,6 +4,7 @@ use borsh::BorshSerialize;
|
||||||
|
|
||||||
/// Warning: This function needs 512+ bytes free on the stack
|
/// Warning: This function needs 512+ bytes free on the stack
|
||||||
pub fn emit_perp_balances(
|
pub fn emit_perp_balances(
|
||||||
|
mango_group: Pubkey,
|
||||||
mango_account: Pubkey,
|
mango_account: Pubkey,
|
||||||
market_index: u64,
|
market_index: u64,
|
||||||
price: i64,
|
price: i64,
|
||||||
|
@ -11,6 +12,7 @@ pub fn emit_perp_balances(
|
||||||
pm: &PerpMarket,
|
pm: &PerpMarket,
|
||||||
) {
|
) {
|
||||||
emit!(PerpBalanceLog {
|
emit!(PerpBalanceLog {
|
||||||
|
mango_group,
|
||||||
mango_account,
|
mango_account,
|
||||||
market_index,
|
market_index,
|
||||||
base_position: pp.base_position_lots,
|
base_position: pp.base_position_lots,
|
||||||
|
@ -25,6 +27,7 @@ pub fn emit_perp_balances(
|
||||||
|
|
||||||
#[event]
|
#[event]
|
||||||
pub struct PerpBalanceLog {
|
pub struct PerpBalanceLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
pub mango_account: Pubkey,
|
pub mango_account: Pubkey,
|
||||||
pub market_index: u64, // IDL doesn't support usize
|
pub market_index: u64, // IDL doesn't support usize
|
||||||
pub base_position: i64,
|
pub base_position: i64,
|
||||||
|
@ -38,6 +41,7 @@ pub struct PerpBalanceLog {
|
||||||
|
|
||||||
#[event]
|
#[event]
|
||||||
pub struct TokenBalanceLog {
|
pub struct TokenBalanceLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
pub mango_account: Pubkey,
|
pub mango_account: Pubkey,
|
||||||
pub token_index: u16, // IDL doesn't support usize
|
pub token_index: u16, // IDL doesn't support usize
|
||||||
pub indexed_position: i128, // on client convert i128 to I80F48 easily by passing in the BN to I80F48 ctor
|
pub indexed_position: i128, // on client convert i128 to I80F48 easily by passing in the BN to I80F48 ctor
|
||||||
|
@ -46,14 +50,6 @@ pub struct TokenBalanceLog {
|
||||||
pub price: i128, // I80F48
|
pub price: i128, // I80F48
|
||||||
}
|
}
|
||||||
|
|
||||||
#[event]
|
|
||||||
pub struct MarginTradeLog {
|
|
||||||
pub mango_account: Pubkey,
|
|
||||||
pub token_indexes: Vec<u16>,
|
|
||||||
pub pre_indexed_positions: Vec<i128>,
|
|
||||||
pub post_indexed_positions: Vec<i128>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
#[derive(AnchorSerialize, AnchorDeserialize)]
|
||||||
pub struct FlashLoanTokenDetail {
|
pub struct FlashLoanTokenDetail {
|
||||||
pub token_index: u16,
|
pub token_index: u16,
|
||||||
|
@ -67,12 +63,14 @@ pub struct FlashLoanTokenDetail {
|
||||||
|
|
||||||
#[event]
|
#[event]
|
||||||
pub struct FlashLoanLog {
|
pub struct FlashLoanLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
pub mango_account: Pubkey,
|
pub mango_account: Pubkey,
|
||||||
pub token_loan_details: Vec<FlashLoanTokenDetail>,
|
pub token_loan_details: Vec<FlashLoanTokenDetail>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[event]
|
#[event]
|
||||||
pub struct WithdrawLog {
|
pub struct WithdrawLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
pub mango_account: Pubkey,
|
pub mango_account: Pubkey,
|
||||||
pub signer: Pubkey,
|
pub signer: Pubkey,
|
||||||
pub token_index: u16,
|
pub token_index: u16,
|
||||||
|
@ -82,6 +80,7 @@ pub struct WithdrawLog {
|
||||||
|
|
||||||
#[event]
|
#[event]
|
||||||
pub struct DepositLog {
|
pub struct DepositLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
pub mango_account: Pubkey,
|
pub mango_account: Pubkey,
|
||||||
pub signer: Pubkey,
|
pub signer: Pubkey,
|
||||||
pub token_index: u16,
|
pub token_index: u16,
|
||||||
|
@ -147,6 +146,7 @@ pub struct UpdateRateLog {
|
||||||
|
|
||||||
#[event]
|
#[event]
|
||||||
pub struct LiquidateTokenAndTokenLog {
|
pub struct LiquidateTokenAndTokenLog {
|
||||||
|
pub mango_group: Pubkey,
|
||||||
pub liqee: Pubkey,
|
pub liqee: Pubkey,
|
||||||
pub liqor: Pubkey,
|
pub liqor: Pubkey,
|
||||||
pub asset_token_index: u16,
|
pub asset_token_index: u16,
|
||||||
|
|
|
@ -82,7 +82,7 @@ pub struct Bank {
|
||||||
// Collection of all fractions-of-native-tokens that got rounded away
|
// Collection of all fractions-of-native-tokens that got rounded away
|
||||||
pub dust: I80F48,
|
pub dust: I80F48,
|
||||||
|
|
||||||
pub flash_loan_vault_initial: u64,
|
pub flash_loan_token_account_initial: u64,
|
||||||
pub flash_loan_approved_amount: u64,
|
pub flash_loan_approved_amount: u64,
|
||||||
|
|
||||||
// Index into TokenInfo on the group
|
// Index into TokenInfo on the group
|
||||||
|
@ -94,11 +94,11 @@ pub struct Bank {
|
||||||
|
|
||||||
pub bank_num: u32,
|
pub bank_num: u32,
|
||||||
|
|
||||||
pub reserved: [u8; 256],
|
pub reserved: [u8; 2560],
|
||||||
}
|
}
|
||||||
const_assert_eq!(
|
const_assert_eq!(
|
||||||
size_of::<Bank>(),
|
size_of::<Bank>(),
|
||||||
32 + 16 + 32 * 3 + 16 + 16 * 6 + 8 * 2 + 16 * 16 + 8 * 2 + 2 + 1 + 1 + 4 + 256
|
32 + 16 + 32 * 3 + 16 + 16 * 6 + 8 * 2 + 16 * 16 + 8 * 2 + 2 + 1 + 1 + 4 + 2560
|
||||||
);
|
);
|
||||||
const_assert_eq!(size_of::<Bank>() % 8, 0);
|
const_assert_eq!(size_of::<Bank>() % 8, 0);
|
||||||
|
|
||||||
|
@ -145,7 +145,10 @@ impl std::fmt::Debug for Bank {
|
||||||
"flash_loan_approved_amount",
|
"flash_loan_approved_amount",
|
||||||
&self.flash_loan_approved_amount,
|
&self.flash_loan_approved_amount,
|
||||||
)
|
)
|
||||||
.field("flash_loan_vault_initial", &self.flash_loan_vault_initial)
|
.field(
|
||||||
|
"flash_loan_token_account_initial",
|
||||||
|
&self.flash_loan_token_account_initial,
|
||||||
|
)
|
||||||
.field("reserved", &self.reserved)
|
.field("reserved", &self.reserved)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
@ -185,11 +188,11 @@ impl Bank {
|
||||||
liquidation_fee: existing_bank.liquidation_fee,
|
liquidation_fee: existing_bank.liquidation_fee,
|
||||||
dust: I80F48::ZERO,
|
dust: I80F48::ZERO,
|
||||||
flash_loan_approved_amount: 0,
|
flash_loan_approved_amount: 0,
|
||||||
flash_loan_vault_initial: u64::MAX,
|
flash_loan_token_account_initial: u64::MAX,
|
||||||
token_index: existing_bank.token_index,
|
token_index: existing_bank.token_index,
|
||||||
bump: existing_bank.bump,
|
bump: existing_bank.bump,
|
||||||
mint_decimals: existing_bank.mint_decimals,
|
mint_decimals: existing_bank.mint_decimals,
|
||||||
reserved: [0; 256],
|
reserved: [0; 2560],
|
||||||
bank_num,
|
bank_num,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,10 +504,16 @@ impl Bank {
|
||||||
// interest rate legs 2 and 3 are seen as punitive legs, encouraging utilization to move towards optimal utilization
|
// interest rate legs 2 and 3 are seen as punitive legs, encouraging utilization to move towards optimal utilization
|
||||||
// lets choose util0 as optimal utilization and 0 to utli0 as the leg where we want the utlization to preferably be
|
// lets choose util0 as optimal utilization and 0 to utli0 as the leg where we want the utlization to preferably be
|
||||||
let optimal_util = self.util0;
|
let optimal_util = self.util0;
|
||||||
// use avg_utilization and not instantaneous_utilization so that rates cannot be manupulated easily
|
// use avg_utilization and not instantaneous_utilization so that rates cannot be manipulated easily
|
||||||
let util_diff = self.avg_utilization - optimal_util;
|
let avg_util = self.avg_utilization;
|
||||||
// move rates up when utilization is above optimal utilization, and vice versa
|
// move rates up when utilization is above optimal utilization, and vice versa
|
||||||
let adjustment = I80F48::ONE + self.adjustment_factor * util_diff;
|
// util factor is between -1 (avg util = 0) and +1 (avg util = 100%)
|
||||||
|
let util_factor = if avg_util > optimal_util {
|
||||||
|
cm!((avg_util - optimal_util) / (I80F48::ONE - optimal_util))
|
||||||
|
} else {
|
||||||
|
cm!((avg_util - optimal_util) / optimal_util)
|
||||||
|
};
|
||||||
|
let adjustment = cm!(I80F48::ONE + self.adjustment_factor * util_factor);
|
||||||
|
|
||||||
// 1. irrespective of which leg current utilization is in, update all rates
|
// 1. irrespective of which leg current utilization is in, update all rates
|
||||||
// 2. only update rates as long as new adjusted rates are above MINIMUM_MAX_RATE,
|
// 2. only update rates as long as new adjusted rates are above MINIMUM_MAX_RATE,
|
||||||
|
|
|
@ -33,9 +33,9 @@ pub struct Group {
|
||||||
|
|
||||||
pub padding2: [u8; 5],
|
pub padding2: [u8; 5],
|
||||||
|
|
||||||
pub reserved: [u8; 256],
|
pub reserved: [u8; 2560],
|
||||||
}
|
}
|
||||||
const_assert_eq!(size_of::<Group>(), 32 * 5 + 4 + 4 + 1 + 1 + 6 + 256);
|
const_assert_eq!(size_of::<Group>(), 32 * 5 + 4 + 4 + 1 + 1 + 6 + 2560);
|
||||||
const_assert_eq!(size_of::<Group>() % 8, 0);
|
const_assert_eq!(size_of::<Group>() % 8, 0);
|
||||||
|
|
||||||
impl Group {
|
impl Group {
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
use arrayref::array_ref;
|
use arrayref::array_ref;
|
||||||
|
|
||||||
use fixed::types::I80F48;
|
use fixed::types::I80F48;
|
||||||
use num_enum::IntoPrimitive;
|
|
||||||
use num_enum::TryFromPrimitive;
|
|
||||||
use solana_program::program_memory::sol_memmove;
|
use solana_program::program_memory::sol_memmove;
|
||||||
use static_assertions::const_assert_eq;
|
use static_assertions::const_assert_eq;
|
||||||
|
|
||||||
|
@ -33,42 +30,6 @@ const BORSH_VEC_PADDING_BYTES: usize = 4;
|
||||||
const BORSH_VEC_SIZE_BYTES: usize = 4;
|
const BORSH_VEC_SIZE_BYTES: usize = 4;
|
||||||
const DEFAULT_MANGO_ACCOUNT_VERSION: u8 = 1;
|
const DEFAULT_MANGO_ACCOUNT_VERSION: u8 = 1;
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Debug,
|
|
||||||
Eq,
|
|
||||||
PartialEq,
|
|
||||||
Clone,
|
|
||||||
Copy,
|
|
||||||
TryFromPrimitive,
|
|
||||||
IntoPrimitive,
|
|
||||||
AnchorSerialize,
|
|
||||||
AnchorDeserialize,
|
|
||||||
)]
|
|
||||||
#[repr(u8)]
|
|
||||||
|
|
||||||
pub enum AccountSize {
|
|
||||||
Small = 0,
|
|
||||||
Large = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for AccountSize {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
AccountSize::Small => write!(f, "Small"),
|
|
||||||
AccountSize::Large => write!(f, "Large"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AccountSize {
|
|
||||||
pub fn space(&self) -> (u8, u8, u8, u8) {
|
|
||||||
match self {
|
|
||||||
AccountSize::Small => (8, 2, 2, 2),
|
|
||||||
AccountSize::Large => (16, 8, 8, 8),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mango Account
|
// Mango Account
|
||||||
// This struct definition is only for clients e.g. typescript, so that they can easily use out of the box
|
// This struct definition is only for clients e.g. typescript, so that they can easily use out of the box
|
||||||
// deserialization and not have to do custom deserialization
|
// deserialization and not have to do custom deserialization
|
||||||
|
@ -158,11 +119,19 @@ impl Default for MangoAccount {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MangoAccount {
|
impl MangoAccount {
|
||||||
pub fn space(account_size: AccountSize) -> usize {
|
pub fn space(
|
||||||
let (token_count, serum3_count, perp_count, perp_oo_count) = account_size.space();
|
token_count: u8,
|
||||||
|
serum3_count: u8,
|
||||||
|
perp_count: u8,
|
||||||
|
perp_oo_count: u8,
|
||||||
|
) -> Result<usize> {
|
||||||
|
require_gte!(16, token_count);
|
||||||
|
require_gte!(8, serum3_count);
|
||||||
|
require_gte!(8, perp_count);
|
||||||
|
require_gte!(64, perp_oo_count);
|
||||||
|
|
||||||
8 + size_of::<MangoAccountFixed>()
|
Ok(8 + size_of::<MangoAccountFixed>()
|
||||||
+ Self::dynamic_size(token_count, serum3_count, perp_count, perp_oo_count)
|
+ Self::dynamic_size(token_count, serum3_count, perp_count, perp_oo_count))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dynamic_token_vec_offset() -> usize {
|
pub fn dynamic_token_vec_offset() -> usize {
|
||||||
|
@ -202,7 +171,7 @@ impl MangoAccount {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dynamic_offsets() {
|
fn test_dynamic_offsets() {
|
||||||
let mut account = MangoAccount::default();
|
let mut account = MangoAccount::default();
|
||||||
account.tokens.resize(16, TokenPosition::default());
|
account.tokens.resize(8, TokenPosition::default());
|
||||||
account.serum3.resize(8, Serum3Orders::default());
|
account.serum3.resize(8, Serum3Orders::default());
|
||||||
account.perps.resize(8, PerpPositions::default());
|
account.perps.resize(8, PerpPositions::default());
|
||||||
account
|
account
|
||||||
|
@ -210,7 +179,7 @@ fn test_dynamic_offsets() {
|
||||||
.resize(8, PerpOpenOrders::default());
|
.resize(8, PerpOpenOrders::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
8 + AnchorSerialize::try_to_vec(&account).unwrap().len(),
|
8 + AnchorSerialize::try_to_vec(&account).unwrap().len(),
|
||||||
MangoAccount::space(AccountSize::Large)
|
MangoAccount::space(8, 8, 8, 8).unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,13 +533,6 @@ impl<
|
||||||
dynamic: self.dynamic(),
|
dynamic: self.dynamic(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> AccountSize {
|
|
||||||
if self.header().perp_count() > 4 {
|
|
||||||
return AccountSize::Large;
|
|
||||||
}
|
|
||||||
AccountSize::Small
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
|
@ -814,7 +776,7 @@ impl<
|
||||||
|
|
||||||
let side = fill.taker_side.invert_side();
|
let side = fill.taker_side.invert_side();
|
||||||
let (base_change, quote_change) = fill.base_quote_change(side);
|
let (base_change, quote_change) = fill.base_quote_change(side);
|
||||||
pa.change_base_position(perp_market, base_change);
|
pa.change_base_and_entry_positions(perp_market, base_change, quote_change);
|
||||||
let quote = I80F48::from_num(
|
let quote = I80F48::from_num(
|
||||||
perp_market
|
perp_market
|
||||||
.quote_lot_size
|
.quote_lot_size
|
||||||
|
@ -856,7 +818,7 @@ impl<
|
||||||
|
|
||||||
let (base_change, quote_change) = fill.base_quote_change(fill.taker_side);
|
let (base_change, quote_change) = fill.base_quote_change(fill.taker_side);
|
||||||
pa.remove_taker_trade(base_change, quote_change);
|
pa.remove_taker_trade(base_change, quote_change);
|
||||||
pa.change_base_position(perp_market, base_change);
|
pa.change_base_and_entry_positions(perp_market, base_change, quote_change);
|
||||||
let quote = I80F48::from_num(perp_market.quote_lot_size * quote_change);
|
let quote = I80F48::from_num(perp_market.quote_lot_size * quote_change);
|
||||||
|
|
||||||
// fees are assessed at time of trade; no need to assess fees here
|
// fees are assessed at time of trade; no need to assess fees here
|
||||||
|
@ -915,14 +877,17 @@ impl<
|
||||||
dst.copy_from_slice(&BorshVecLength::from(count).to_le_bytes());
|
dst.copy_from_slice(&BorshVecLength::from(count).to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_dynamic_content(&mut self, account_size: AccountSize) -> Result<()> {
|
pub fn expand_dynamic_content(
|
||||||
let (new_token_count, new_serum3_count, new_perp_count, new_perp_oo_count) =
|
&mut self,
|
||||||
account_size.space();
|
new_token_count: u8,
|
||||||
|
new_serum3_count: u8,
|
||||||
require_gt!(new_token_count, self.header().token_count);
|
new_perp_count: u8,
|
||||||
require_gt!(new_serum3_count, self.header().serum3_count);
|
new_perp_oo_count: u8,
|
||||||
require_gt!(new_perp_count, self.header().perp_count);
|
) -> Result<()> {
|
||||||
require_gt!(new_perp_oo_count, self.header().perp_oo_count);
|
require_gte!(new_token_count, self.header().token_count);
|
||||||
|
require_gte!(new_serum3_count, self.header().serum3_count);
|
||||||
|
require_gte!(new_perp_count, self.header().perp_count);
|
||||||
|
require_gte!(new_perp_oo_count, self.header().perp_oo_count);
|
||||||
|
|
||||||
// create a temp copy to compute new starting offsets
|
// create a temp copy to compute new starting offsets
|
||||||
let new_header = MangoAccountDynamicHeader {
|
let new_header = MangoAccountDynamicHeader {
|
||||||
|
@ -937,6 +902,7 @@ impl<
|
||||||
// expand dynamic components by first moving existing positions, and then setting new ones to defaults
|
// expand dynamic components by first moving existing positions, and then setting new ones to defaults
|
||||||
|
|
||||||
// perp oo
|
// perp oo
|
||||||
|
if new_header.perp_oo_count() > old_header.perp_oo_count() {
|
||||||
unsafe {
|
unsafe {
|
||||||
sol_memmove(
|
sol_memmove(
|
||||||
&mut dynamic[new_header.perp_oo_offset(0)],
|
&mut dynamic[new_header.perp_oo_offset(0)],
|
||||||
|
@ -948,8 +914,10 @@ impl<
|
||||||
*get_helper_mut(dynamic, new_header.perp_oo_offset(i.into())) =
|
*get_helper_mut(dynamic, new_header.perp_oo_offset(i.into())) =
|
||||||
PerpOpenOrders::default();
|
PerpOpenOrders::default();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// perp positions
|
// perp positions
|
||||||
|
if new_header.perp_count() > old_header.perp_count() {
|
||||||
unsafe {
|
unsafe {
|
||||||
sol_memmove(
|
sol_memmove(
|
||||||
&mut dynamic[new_header.perp_offset(0)],
|
&mut dynamic[new_header.perp_offset(0)],
|
||||||
|
@ -958,10 +926,13 @@ impl<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for i in old_header.perp_count..new_perp_count {
|
for i in old_header.perp_count..new_perp_count {
|
||||||
*get_helper_mut(dynamic, new_header.perp_offset(i.into())) = PerpPositions::default();
|
*get_helper_mut(dynamic, new_header.perp_offset(i.into())) =
|
||||||
|
PerpPositions::default();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// serum3 positions
|
// serum3 positions
|
||||||
|
if new_header.serum3_count() > old_header.serum3_count() {
|
||||||
unsafe {
|
unsafe {
|
||||||
sol_memmove(
|
sol_memmove(
|
||||||
&mut dynamic[new_header.serum3_offset(0)],
|
&mut dynamic[new_header.serum3_offset(0)],
|
||||||
|
@ -970,10 +941,13 @@ impl<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for i in old_header.serum3_count..new_serum3_count {
|
for i in old_header.serum3_count..new_serum3_count {
|
||||||
*get_helper_mut(dynamic, new_header.serum3_offset(i.into())) = Serum3Orders::default();
|
*get_helper_mut(dynamic, new_header.serum3_offset(i.into())) =
|
||||||
|
Serum3Orders::default();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// token positions
|
// token positions
|
||||||
|
if new_header.token_count() > old_header.token_count() {
|
||||||
unsafe {
|
unsafe {
|
||||||
sol_memmove(
|
sol_memmove(
|
||||||
&mut dynamic[new_header.token_offset(0)],
|
&mut dynamic[new_header.token_offset(0)],
|
||||||
|
@ -982,7 +956,9 @@ impl<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for i in old_header.token_count..new_token_count {
|
for i in old_header.token_count..new_token_count {
|
||||||
*get_helper_mut(dynamic, new_header.token_offset(i.into())) = TokenPosition::default();
|
*get_helper_mut(dynamic, new_header.token_offset(i.into())) =
|
||||||
|
TokenPosition::default();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// update header
|
// update header
|
||||||
|
|
|
@ -148,6 +148,11 @@ pub struct PerpPositions {
|
||||||
/// measured in native quote
|
/// measured in native quote
|
||||||
pub quote_position_native: I80F48,
|
pub quote_position_native: I80F48,
|
||||||
|
|
||||||
|
/// Tracks what the position is to calculate average entry & break even price
|
||||||
|
pub base_entry_lots: i64,
|
||||||
|
pub quote_entry_native: i64,
|
||||||
|
pub quote_exit_native: i64,
|
||||||
|
|
||||||
/// Already settled funding
|
/// Already settled funding
|
||||||
pub long_settled_funding: I80F48,
|
pub long_settled_funding: I80F48,
|
||||||
pub short_settled_funding: I80F48,
|
pub short_settled_funding: I80F48,
|
||||||
|
@ -180,7 +185,7 @@ impl std::fmt::Debug for PerpPositions {
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const_assert_eq!(size_of::<PerpPositions>(), 8 + 8 * 5 + 3 * 16 + 64);
|
const_assert_eq!(size_of::<PerpPositions>(), 8 + 8 * 8 + 3 * 16 + 64);
|
||||||
const_assert_eq!(size_of::<PerpPositions>() % 8, 0);
|
const_assert_eq!(size_of::<PerpPositions>() % 8, 0);
|
||||||
|
|
||||||
unsafe impl bytemuck::Pod for PerpPositions {}
|
unsafe impl bytemuck::Pod for PerpPositions {}
|
||||||
|
@ -192,6 +197,9 @@ impl Default for PerpPositions {
|
||||||
market_index: PerpMarketIndex::MAX,
|
market_index: PerpMarketIndex::MAX,
|
||||||
base_position_lots: 0,
|
base_position_lots: 0,
|
||||||
quote_position_native: I80F48::ZERO,
|
quote_position_native: I80F48::ZERO,
|
||||||
|
base_entry_lots: 0,
|
||||||
|
quote_entry_native: 0,
|
||||||
|
quote_exit_native: 0,
|
||||||
bids_base_lots: 0,
|
bids_base_lots: 0,
|
||||||
asks_base_lots: 0,
|
asks_base_lots: 0,
|
||||||
taker_base_lots: 0,
|
taker_base_lots: 0,
|
||||||
|
@ -257,6 +265,67 @@ impl PerpPositions {
|
||||||
self.long_settled_funding = perp_market.long_funding;
|
self.long_settled_funding = perp_market.long_funding;
|
||||||
self.short_settled_funding = perp_market.short_funding;
|
self.short_settled_funding = perp_market.short_funding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the quote entry position
|
||||||
|
pub fn change_quote_entry(&mut self, base_change: i64, quote_change: i64) {
|
||||||
|
if base_change == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let old_position = self.base_position_lots;
|
||||||
|
let is_increasing = old_position == 0 || old_position.signum() == base_change.signum();
|
||||||
|
match is_increasing {
|
||||||
|
true => {
|
||||||
|
self.quote_entry_native = cm!(self.quote_entry_native + quote_change);
|
||||||
|
self.base_entry_lots = cm!(self.base_entry_lots + base_change);
|
||||||
|
}
|
||||||
|
false => {
|
||||||
|
let new_position = cm!(old_position + base_change);
|
||||||
|
self.quote_exit_native = cm!(self.quote_exit_native + quote_change);
|
||||||
|
let is_overflow = old_position.signum() == -new_position.signum();
|
||||||
|
if new_position == 0 {
|
||||||
|
self.quote_entry_native = 0;
|
||||||
|
self.quote_exit_native = 0;
|
||||||
|
self.base_entry_lots = 0;
|
||||||
|
}
|
||||||
|
if is_overflow {
|
||||||
|
self.quote_entry_native = cm!(((new_position as f64) * (quote_change as f64)
|
||||||
|
/ (base_change as f64))
|
||||||
|
.round()) as i64;
|
||||||
|
self.quote_exit_native = 0;
|
||||||
|
self.base_entry_lots = new_position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change the base and quote positions as the result of a trade
|
||||||
|
pub fn change_base_and_entry_positions(
|
||||||
|
&mut self,
|
||||||
|
perp_market: &mut PerpMarket,
|
||||||
|
base_change: i64,
|
||||||
|
quote_change: i64,
|
||||||
|
) {
|
||||||
|
self.change_quote_entry(base_change, quote_change);
|
||||||
|
self.change_base_position(perp_market, base_change);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the average entry price of the position
|
||||||
|
pub fn get_avg_entry_price(&self) -> I80F48 {
|
||||||
|
if self.base_entry_lots == 0 {
|
||||||
|
return I80F48::ZERO; // TODO: What should this actually return? Error? NaN?
|
||||||
|
}
|
||||||
|
(I80F48::from(self.quote_entry_native) / I80F48::from(self.base_entry_lots)).abs()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the break even price of the position
|
||||||
|
pub fn get_break_even_price(&self) -> I80F48 {
|
||||||
|
if self.base_position_lots == 0 {
|
||||||
|
return I80F48::ZERO; // TODO: What should this actually return? Error? NaN?
|
||||||
|
}
|
||||||
|
(I80F48::from(self.quote_entry_native + self.quote_exit_native)
|
||||||
|
/ I80F48::from(self.base_position_lots))
|
||||||
|
.abs()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[zero_copy]
|
#[zero_copy]
|
||||||
|
@ -305,3 +374,227 @@ macro_rules! account_seeds {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use account_seeds;
|
pub use account_seeds;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::state::{OracleConfig, PerpMarket};
|
||||||
|
use anchor_lang::prelude::Pubkey;
|
||||||
|
use fixed::types::I80F48;
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use super::PerpPositions;
|
||||||
|
|
||||||
|
fn create_perp_position(base_pos: i64, quote_pos: i64, entry_pos: i64) -> PerpPositions {
|
||||||
|
let mut pos = PerpPositions::default();
|
||||||
|
pos.base_position_lots = base_pos;
|
||||||
|
pos.quote_position_native = I80F48::from(quote_pos);
|
||||||
|
pos.quote_entry_native = entry_pos;
|
||||||
|
pos.quote_exit_native = 0;
|
||||||
|
pos.base_entry_lots = base_pos;
|
||||||
|
pos
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_perp_market() -> PerpMarket {
|
||||||
|
return PerpMarket {
|
||||||
|
group: Pubkey::new_unique(),
|
||||||
|
base_token_index: 0,
|
||||||
|
perp_market_index: 0,
|
||||||
|
name: Default::default(),
|
||||||
|
oracle: Pubkey::new_unique(),
|
||||||
|
oracle_config: OracleConfig {
|
||||||
|
conf_filter: I80F48::ZERO,
|
||||||
|
},
|
||||||
|
bids: Pubkey::new_unique(),
|
||||||
|
asks: Pubkey::new_unique(),
|
||||||
|
event_queue: Pubkey::new_unique(),
|
||||||
|
quote_lot_size: 1,
|
||||||
|
base_lot_size: 1,
|
||||||
|
maint_asset_weight: I80F48::from(1),
|
||||||
|
init_asset_weight: I80F48::from(1),
|
||||||
|
maint_liab_weight: I80F48::from(1),
|
||||||
|
init_liab_weight: I80F48::from(1),
|
||||||
|
liquidation_fee: I80F48::ZERO,
|
||||||
|
maker_fee: I80F48::ZERO,
|
||||||
|
taker_fee: I80F48::ZERO,
|
||||||
|
min_funding: I80F48::ZERO,
|
||||||
|
max_funding: I80F48::ZERO,
|
||||||
|
impact_quantity: 0,
|
||||||
|
long_funding: I80F48::ZERO,
|
||||||
|
short_funding: I80F48::ZERO,
|
||||||
|
funding_last_updated: 0,
|
||||||
|
open_interest: 0,
|
||||||
|
seq_num: 0,
|
||||||
|
fees_accrued: I80F48::ZERO,
|
||||||
|
bump: 0,
|
||||||
|
base_token_decimals: 0,
|
||||||
|
reserved: [0; 128],
|
||||||
|
padding1: Default::default(),
|
||||||
|
padding2: Default::default(),
|
||||||
|
registration_time: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_long_increasing_from_zero() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(0, 0, 0);
|
||||||
|
// Go long 10 @ 10
|
||||||
|
pos.change_base_and_entry_positions(&mut market, 10, -100);
|
||||||
|
assert_eq!(pos.quote_entry_native, -100);
|
||||||
|
assert_eq!(pos.get_avg_entry_price(), I80F48::from(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_short_increasing_from_zero() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(0, 0, 0);
|
||||||
|
// Go short 10 @ 10
|
||||||
|
pos.change_base_and_entry_positions(&mut market, -10, 100);
|
||||||
|
assert_eq!(pos.quote_entry_native, 100);
|
||||||
|
assert_eq!(pos.get_avg_entry_price(), I80F48::from(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_long_increasing_from_long() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(10, -100, -100);
|
||||||
|
// Go long 10 @ 30
|
||||||
|
pos.change_base_and_entry_positions(&mut market, 10, -300);
|
||||||
|
assert_eq!(pos.quote_entry_native, -400);
|
||||||
|
assert_eq!(pos.get_avg_entry_price(), I80F48::from(20));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_short_increasing_from_short() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(-10, 100, 100);
|
||||||
|
// Go short 10 @ 10
|
||||||
|
pos.change_base_and_entry_positions(&mut market, -10, 300);
|
||||||
|
assert_eq!(pos.quote_entry_native, 400);
|
||||||
|
assert_eq!(pos.get_avg_entry_price(), I80F48::from(20));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_long_decreasing_from_short() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(-10, 100, 100);
|
||||||
|
// Go long 5 @ 50
|
||||||
|
pos.change_base_and_entry_positions(&mut market, 5, 250);
|
||||||
|
assert_eq!(pos.quote_entry_native, 100);
|
||||||
|
assert_eq!(pos.base_entry_lots, -10);
|
||||||
|
assert_eq!(pos.quote_exit_native, 250);
|
||||||
|
assert_eq!(pos.get_avg_entry_price(), I80F48::from(10)); // Entry price remains the same when decreasing
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_short_decreasing_from_long() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(10, -100, -100);
|
||||||
|
// Go short 5 @ 50
|
||||||
|
pos.change_base_and_entry_positions(&mut market, -5, -250);
|
||||||
|
assert_eq!(pos.quote_entry_native, -100);
|
||||||
|
assert_eq!(pos.base_entry_lots, 10);
|
||||||
|
assert_eq!(pos.quote_exit_native, -250);
|
||||||
|
assert_eq!(pos.get_avg_entry_price(), I80F48::from(10)); // Entry price remains the same when decreasing
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_long_close_with_short() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(10, -100, -100);
|
||||||
|
// Go short 10 @ 50
|
||||||
|
pos.change_base_and_entry_positions(&mut market, -10, 250);
|
||||||
|
assert_eq!(pos.quote_entry_native, 0);
|
||||||
|
assert_eq!(pos.quote_exit_native, 0);
|
||||||
|
assert_eq!(pos.base_entry_lots, 0);
|
||||||
|
assert_eq!(pos.get_avg_entry_price(), I80F48::from(0)); // Entry price zero when no position
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_short_close_with_long() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(-10, 100, 100);
|
||||||
|
// Go long 10 @ 50
|
||||||
|
pos.change_base_and_entry_positions(&mut market, 10, -250);
|
||||||
|
assert_eq!(pos.quote_entry_native, 0);
|
||||||
|
assert_eq!(pos.quote_exit_native, 0);
|
||||||
|
assert_eq!(pos.base_entry_lots, 0);
|
||||||
|
assert_eq!(pos.get_avg_entry_price(), I80F48::from(0)); // Entry price zero when no position
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_long_close_short_with_overflow() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(10, -100, -100);
|
||||||
|
// Go short 15 @ 20
|
||||||
|
pos.change_base_and_entry_positions(&mut market, -15, 300);
|
||||||
|
assert_eq!(pos.quote_entry_native, 100);
|
||||||
|
assert_eq!(pos.quote_exit_native, 0);
|
||||||
|
assert_eq!(pos.base_entry_lots, -5);
|
||||||
|
assert_eq!(pos.get_avg_entry_price(), I80F48::from(20)); // Entry price zero when no position
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_short_close_long_with_overflow() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(-10, 100, 100);
|
||||||
|
// Go short 15 @ 20
|
||||||
|
pos.change_base_and_entry_positions(&mut market, 15, -300);
|
||||||
|
assert_eq!(pos.quote_entry_native, -100);
|
||||||
|
assert_eq!(pos.quote_exit_native, 0);
|
||||||
|
assert_eq!(pos.base_entry_lots, 5);
|
||||||
|
assert_eq!(pos.get_avg_entry_price(), I80F48::from(20)); // Entry price zero when no position
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_break_even_price() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(0, 0, 0);
|
||||||
|
// Buy 11 @ 10,000
|
||||||
|
pos.change_base_and_entry_positions(&mut market, 11, -11 * 10_000);
|
||||||
|
// Sell 1 @ 12,000
|
||||||
|
pos.change_base_and_entry_positions(&mut market, -1, 12_000);
|
||||||
|
assert_eq!(pos.quote_entry_native, -11 * 10_000);
|
||||||
|
assert_eq!(pos.quote_exit_native, 12_000);
|
||||||
|
assert_eq!(pos.base_entry_lots, 11);
|
||||||
|
assert_eq!(pos.base_position_lots, 10);
|
||||||
|
assert_eq!(pos.get_break_even_price(), I80F48::from(9_800)); // We made 2k on the trade, so we can sell our contract up to a loss of 200 each
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_quote_entry_multiple_and_reversed_changes_return_entry_to_zero() {
|
||||||
|
let mut market = create_perp_market();
|
||||||
|
let mut pos = create_perp_position(0, 0, 0);
|
||||||
|
|
||||||
|
// Generate array of random trades
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let mut trades: Vec<[i64; 2]> = Vec::with_capacity(500);
|
||||||
|
for _ in 0..trades.capacity() {
|
||||||
|
let qty: i64 = rng.gen_range(-1000..=1000);
|
||||||
|
let px: f64 = rng.gen_range(0.1..=100.0);
|
||||||
|
let quote: i64 = (-qty as f64 * px).round() as i64;
|
||||||
|
trades.push([qty, quote]);
|
||||||
|
}
|
||||||
|
// Apply all of the trades going forward
|
||||||
|
trades.iter().for_each(|[qty, quote]| {
|
||||||
|
pos.change_base_and_entry_positions(&mut market, *qty, *quote);
|
||||||
|
});
|
||||||
|
// base_position should be sum of all base quantities
|
||||||
|
assert_eq!(
|
||||||
|
pos.base_position_lots,
|
||||||
|
trades.iter().map(|[qty, _]| qty).sum::<i64>()
|
||||||
|
);
|
||||||
|
// Reverse out all the trades
|
||||||
|
trades.iter().for_each(|[qty, quote]| {
|
||||||
|
pos.change_base_and_entry_positions(&mut market, -*qty, -*quote);
|
||||||
|
});
|
||||||
|
// base position should be 0
|
||||||
|
assert_eq!(pos.base_position_lots, 0);
|
||||||
|
// quote entry position should be 0
|
||||||
|
assert_eq!(pos.quote_entry_native, 0);
|
||||||
|
// quote exit should be 0
|
||||||
|
assert_eq!(pos.quote_exit_native, 0);
|
||||||
|
// base entry lots should be 0
|
||||||
|
assert_eq!(pos.base_entry_lots, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,20 +21,20 @@ pub struct MintInfo {
|
||||||
// ABI: Clients rely on this being at offset 40
|
// ABI: Clients rely on this being at offset 40
|
||||||
pub token_index: TokenIndex,
|
pub token_index: TokenIndex,
|
||||||
|
|
||||||
pub padding1: [u8; 6],
|
pub group_insurance_fund: u8,
|
||||||
|
pub padding1: [u8; 5],
|
||||||
pub mint: Pubkey,
|
pub mint: Pubkey,
|
||||||
pub banks: [Pubkey; MAX_BANKS],
|
pub banks: [Pubkey; MAX_BANKS],
|
||||||
pub vaults: [Pubkey; MAX_BANKS],
|
pub vaults: [Pubkey; MAX_BANKS],
|
||||||
pub oracle: Pubkey,
|
pub oracle: Pubkey,
|
||||||
|
|
||||||
pub registration_time: i64,
|
pub registration_time: i64,
|
||||||
pub group_insurance_fund: u8,
|
|
||||||
|
|
||||||
pub reserved: [u8; 255],
|
pub reserved: [u8; 2560],
|
||||||
}
|
}
|
||||||
const_assert_eq!(
|
const_assert_eq!(
|
||||||
size_of::<MintInfo>(),
|
size_of::<MintInfo>(),
|
||||||
MAX_BANKS * 2 * 32 + 3 * 32 + 2 + 8 + 6 + 1 + 255
|
MAX_BANKS * 2 * 32 + 3 * 32 + 2 + 8 + 6 + 2560
|
||||||
);
|
);
|
||||||
const_assert_eq!(size_of::<MintInfo>() % 8, 0);
|
const_assert_eq!(size_of::<MintInfo>() % 8, 0);
|
||||||
|
|
||||||
|
|
|
@ -1083,7 +1083,10 @@ impl<'keypair> ClientInstruction for GroupCloseInstruction<'keypair> {
|
||||||
|
|
||||||
pub struct AccountCreateInstruction<'keypair> {
|
pub struct AccountCreateInstruction<'keypair> {
|
||||||
pub account_num: u32,
|
pub account_num: u32,
|
||||||
pub account_size: AccountSize,
|
pub token_count: u8,
|
||||||
|
pub serum3_count: u8,
|
||||||
|
pub perp_count: u8,
|
||||||
|
pub perp_oo_count: u8,
|
||||||
pub group: Pubkey,
|
pub group: Pubkey,
|
||||||
pub owner: &'keypair Keypair,
|
pub owner: &'keypair Keypair,
|
||||||
pub payer: &'keypair Keypair,
|
pub payer: &'keypair Keypair,
|
||||||
|
@ -1099,7 +1102,10 @@ impl<'keypair> ClientInstruction for AccountCreateInstruction<'keypair> {
|
||||||
let program_id = mango_v4::id();
|
let program_id = mango_v4::id();
|
||||||
let instruction = mango_v4::instruction::AccountCreate {
|
let instruction = mango_v4::instruction::AccountCreate {
|
||||||
account_num: self.account_num,
|
account_num: self.account_num,
|
||||||
account_size: self.account_size,
|
token_count: self.token_count,
|
||||||
|
serum3_count: self.serum3_count,
|
||||||
|
perp_count: self.perp_count,
|
||||||
|
perp_oo_count: self.perp_oo_count,
|
||||||
name: "my_mango_account".to_string(),
|
name: "my_mango_account".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1136,6 +1142,10 @@ pub struct AccountExpandInstruction<'keypair> {
|
||||||
pub group: Pubkey,
|
pub group: Pubkey,
|
||||||
pub owner: &'keypair Keypair,
|
pub owner: &'keypair Keypair,
|
||||||
pub payer: &'keypair Keypair,
|
pub payer: &'keypair Keypair,
|
||||||
|
pub token_count: u8,
|
||||||
|
pub serum3_count: u8,
|
||||||
|
pub perp_count: u8,
|
||||||
|
pub perp_oo_count: u8,
|
||||||
}
|
}
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl<'keypair> ClientInstruction for AccountExpandInstruction<'keypair> {
|
impl<'keypair> ClientInstruction for AccountExpandInstruction<'keypair> {
|
||||||
|
@ -1146,7 +1156,12 @@ impl<'keypair> ClientInstruction for AccountExpandInstruction<'keypair> {
|
||||||
_account_loader: impl ClientAccountLoader + 'async_trait,
|
_account_loader: impl ClientAccountLoader + 'async_trait,
|
||||||
) -> (Self::Accounts, instruction::Instruction) {
|
) -> (Self::Accounts, instruction::Instruction) {
|
||||||
let program_id = mango_v4::id();
|
let program_id = mango_v4::id();
|
||||||
let instruction = mango_v4::instruction::AccountExpand {};
|
let instruction = mango_v4::instruction::AccountExpand {
|
||||||
|
token_count: self.token_count,
|
||||||
|
serum3_count: self.serum3_count,
|
||||||
|
perp_count: self.perp_count,
|
||||||
|
perp_oo_count: self.perp_oo_count,
|
||||||
|
};
|
||||||
|
|
||||||
let account = Pubkey::find_program_address(
|
let account = Pubkey::find_program_address(
|
||||||
&[
|
&[
|
||||||
|
@ -2374,6 +2389,7 @@ impl ClientInstruction for PerpConsumeEventsInstruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PerpUpdateFundingInstruction {
|
pub struct PerpUpdateFundingInstruction {
|
||||||
|
pub group: Pubkey,
|
||||||
pub perp_market: Pubkey,
|
pub perp_market: Pubkey,
|
||||||
pub bids: Pubkey,
|
pub bids: Pubkey,
|
||||||
pub asks: Pubkey,
|
pub asks: Pubkey,
|
||||||
|
@ -2391,6 +2407,7 @@ impl ClientInstruction for PerpUpdateFundingInstruction {
|
||||||
let program_id = mango_v4::id();
|
let program_id = mango_v4::id();
|
||||||
let instruction = Self::Instruction {};
|
let instruction = Self::Instruction {};
|
||||||
let accounts = Self::Accounts {
|
let accounts = Self::Accounts {
|
||||||
|
group: self.group,
|
||||||
perp_market: self.perp_market,
|
perp_market: self.perp_market,
|
||||||
bids: self.bids,
|
bids: self.bids,
|
||||||
asks: self.asks,
|
asks: self.asks,
|
||||||
|
@ -2444,6 +2461,7 @@ impl ClientInstruction for TokenUpdateIndexAndRateInstruction {
|
||||||
let mint_info: MintInfo = loader.load(&self.mint_info).await.unwrap();
|
let mint_info: MintInfo = loader.load(&self.mint_info).await.unwrap();
|
||||||
|
|
||||||
let accounts = Self::Accounts {
|
let accounts = Self::Accounts {
|
||||||
|
group: mint_info.group,
|
||||||
mint_info: self.mint_info,
|
mint_info: self.mint_info,
|
||||||
oracle: mint_info.oracle,
|
oracle: mint_info.oracle,
|
||||||
instructions: solana_program::sysvar::instructions::id(),
|
instructions: solana_program::sysvar::instructions::id(),
|
||||||
|
|
|
@ -44,7 +44,10 @@ async fn test_bankrupt_tokens_socialize_loss() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 2,
|
account_num: 2,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -90,7 +93,10 @@ async fn test_bankrupt_tokens_socialize_loss() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -353,7 +359,10 @@ async fn test_bankrupt_tokens_insurance_fund() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 2,
|
account_num: 2,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -399,7 +408,10 @@ async fn test_bankrupt_tokens_insurance_fund() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
|
|
@ -41,7 +41,10 @@ async fn test_basic() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Small,
|
token_count: 8,
|
||||||
|
serum3_count: 0,
|
||||||
|
perp_count: 0,
|
||||||
|
perp_oo_count: 0,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -55,6 +58,10 @@ async fn test_basic() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountExpandInstruction {
|
AccountExpandInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
|
|
@ -37,7 +37,10 @@ async fn test_delegate() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
|
|
@ -37,7 +37,10 @@ async fn test_health_compute_tokens() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -102,7 +105,10 @@ async fn test_health_compute_serum() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -214,7 +220,10 @@ async fn test_health_compute_perp() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
|
|
@ -42,7 +42,10 @@ async fn test_liq_tokens_force_cancel() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 2,
|
account_num: 2,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -98,7 +101,10 @@ async fn test_liq_tokens_force_cancel() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -253,7 +259,10 @@ async fn test_liq_tokens_with_token() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 2,
|
account_num: 2,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -284,7 +293,10 @@ async fn test_liq_tokens_with_token() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
|
|
@ -51,7 +51,10 @@ async fn test_margin_trade() -> Result<(), BanksClientError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 1,
|
account_num: 1,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -94,7 +97,10 @@ async fn test_margin_trade() -> Result<(), BanksClientError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
|
|
@ -36,7 +36,10 @@ async fn test_perp() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -50,7 +53,10 @@ async fn test_perp() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 1,
|
account_num: 1,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
|
|
@ -38,7 +38,10 @@ async fn test_position_lifetime() -> Result<()> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -52,7 +55,10 @@ async fn test_position_lifetime() -> Result<()> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 1,
|
account_num: 1,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
|
|
@ -40,7 +40,10 @@ async fn test_serum() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
|
|
@ -36,7 +36,10 @@ async fn test_token_update_index_and_rate() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
@ -64,7 +67,10 @@ async fn test_token_update_index_and_rate() -> Result<(), TransportError> {
|
||||||
solana,
|
solana,
|
||||||
AccountCreateInstruction {
|
AccountCreateInstruction {
|
||||||
account_num: 1,
|
account_num: 1,
|
||||||
account_size: AccountSize::Large,
|
token_count: 16,
|
||||||
|
serum3_count: 8,
|
||||||
|
perp_count: 8,
|
||||||
|
perp_oo_count: 8,
|
||||||
group,
|
group,
|
||||||
owner,
|
owner,
|
||||||
payer,
|
payer,
|
||||||
|
|
|
@ -63,7 +63,7 @@ export class Bank {
|
||||||
initLiabWeight: I80F48Dto;
|
initLiabWeight: I80F48Dto;
|
||||||
liquidationFee: I80F48Dto;
|
liquidationFee: I80F48Dto;
|
||||||
dust: I80F48Dto;
|
dust: I80F48Dto;
|
||||||
flashLoanVaultInitial: BN;
|
flashLoanTokenAccountInitial: BN;
|
||||||
flashLoanApprovedAmount: BN;
|
flashLoanApprovedAmount: BN;
|
||||||
tokenIndex: number;
|
tokenIndex: number;
|
||||||
mintDecimals: number;
|
mintDecimals: number;
|
||||||
|
@ -102,7 +102,7 @@ export class Bank {
|
||||||
obj.initLiabWeight,
|
obj.initLiabWeight,
|
||||||
obj.liquidationFee,
|
obj.liquidationFee,
|
||||||
obj.dust,
|
obj.dust,
|
||||||
obj.flashLoanVaultInitial,
|
obj.flashLoanTokenAccountInitial,
|
||||||
obj.flashLoanApprovedAmount,
|
obj.flashLoanApprovedAmount,
|
||||||
obj.tokenIndex,
|
obj.tokenIndex,
|
||||||
obj.mintDecimals,
|
obj.mintDecimals,
|
||||||
|
@ -142,7 +142,7 @@ export class Bank {
|
||||||
initLiabWeight: I80F48Dto,
|
initLiabWeight: I80F48Dto,
|
||||||
liquidationFee: I80F48Dto,
|
liquidationFee: I80F48Dto,
|
||||||
dust: I80F48Dto,
|
dust: I80F48Dto,
|
||||||
flashLoanVaultInitial: BN,
|
flashLoanTokenAccountInitial: BN,
|
||||||
flashLoanApprovedAmount: BN,
|
flashLoanApprovedAmount: BN,
|
||||||
public tokenIndex: number,
|
public tokenIndex: number,
|
||||||
public mintDecimals: number,
|
public mintDecimals: number,
|
||||||
|
|
|
@ -594,8 +594,3 @@ export class EquityDto {
|
||||||
tokens: { tokenIndex: number; value: I80F48Dto }[];
|
tokens: { tokenIndex: number; value: I80F48Dto }[];
|
||||||
perps: { perpMarketIndex: number; value: I80F48Dto }[];
|
perps: { perpMarketIndex: number; value: I80F48Dto }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AccountSize {
|
|
||||||
static small = { small: {} };
|
|
||||||
static large = { large: {} };
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,11 +30,7 @@ import bs58 from 'bs58';
|
||||||
import { Bank, MintInfo } from './accounts/bank';
|
import { Bank, MintInfo } from './accounts/bank';
|
||||||
import { Group } from './accounts/group';
|
import { Group } from './accounts/group';
|
||||||
import { I80F48 } from './accounts/I80F48';
|
import { I80F48 } from './accounts/I80F48';
|
||||||
import {
|
import { MangoAccount, MangoAccountData } from './accounts/mangoAccount';
|
||||||
AccountSize,
|
|
||||||
MangoAccount,
|
|
||||||
MangoAccountData,
|
|
||||||
} from './accounts/mangoAccount';
|
|
||||||
import { StubOracle } from './accounts/oracle';
|
import { StubOracle } from './accounts/oracle';
|
||||||
import { OrderType, PerpMarket, Side } from './accounts/perp';
|
import { OrderType, PerpMarket, Side } from './accounts/perp';
|
||||||
import {
|
import {
|
||||||
|
@ -236,6 +232,7 @@ export class MangoClient {
|
||||||
tokenName: string,
|
tokenName: string,
|
||||||
oracle: PublicKey,
|
oracle: PublicKey,
|
||||||
oracleConfFilter: number,
|
oracleConfFilter: number,
|
||||||
|
groupInsuranceFund: boolean,
|
||||||
adjustmentFactor: number,
|
adjustmentFactor: number,
|
||||||
util0: number,
|
util0: number,
|
||||||
rate0: number,
|
rate0: number,
|
||||||
|
@ -262,6 +259,7 @@ export class MangoClient {
|
||||||
val: I80F48.fromNumber(oracleConfFilter).getData(),
|
val: I80F48.fromNumber(oracleConfFilter).getData(),
|
||||||
},
|
},
|
||||||
} as any, // future: nested custom types dont typecheck, fix if possible?
|
} as any, // future: nested custom types dont typecheck, fix if possible?
|
||||||
|
groupInsuranceFund,
|
||||||
{ adjustmentFactor, util0, rate0, util1, rate1, maxRate },
|
{ adjustmentFactor, util0, rate0, util1, rate1, maxRate },
|
||||||
loanFeeRate,
|
loanFeeRate,
|
||||||
loanOriginationFeeRate,
|
loanOriginationFeeRate,
|
||||||
|
@ -467,12 +465,11 @@ export class MangoClient {
|
||||||
group: Group,
|
group: Group,
|
||||||
ownerPk: PublicKey,
|
ownerPk: PublicKey,
|
||||||
accountNumber?: number,
|
accountNumber?: number,
|
||||||
accountSize?: AccountSize,
|
|
||||||
name?: string,
|
name?: string,
|
||||||
): Promise<MangoAccount> {
|
): Promise<MangoAccount> {
|
||||||
let mangoAccounts = await this.getMangoAccountsForOwner(group, ownerPk);
|
let mangoAccounts = await this.getMangoAccountsForOwner(group, ownerPk);
|
||||||
if (mangoAccounts.length === 0) {
|
if (mangoAccounts.length === 0) {
|
||||||
await this.createMangoAccount(group, accountNumber, accountSize, name);
|
await this.createMangoAccount(group, accountNumber, name);
|
||||||
mangoAccounts = await this.getMangoAccountsForOwner(group, ownerPk);
|
mangoAccounts = await this.getMangoAccountsForOwner(group, ownerPk);
|
||||||
}
|
}
|
||||||
return mangoAccounts[0];
|
return mangoAccounts[0];
|
||||||
|
@ -481,15 +478,10 @@ export class MangoClient {
|
||||||
public async createMangoAccount(
|
public async createMangoAccount(
|
||||||
group: Group,
|
group: Group,
|
||||||
accountNumber?: number,
|
accountNumber?: number,
|
||||||
accountSize?: AccountSize,
|
|
||||||
name?: string,
|
name?: string,
|
||||||
): Promise<TransactionSignature> {
|
): Promise<TransactionSignature> {
|
||||||
return await this.program.methods
|
return await this.program.methods
|
||||||
.accountCreate(
|
.accountCreate(accountNumber ?? 0, 8, 0, 0, 0, name ?? '')
|
||||||
accountNumber ?? 0,
|
|
||||||
accountSize ?? AccountSize.small,
|
|
||||||
name ?? '',
|
|
||||||
)
|
|
||||||
.accounts({
|
.accounts({
|
||||||
group: group.publicKey,
|
group: group.publicKey,
|
||||||
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
||||||
|
@ -501,9 +493,13 @@ export class MangoClient {
|
||||||
public async expandMangoAccount(
|
public async expandMangoAccount(
|
||||||
group: Group,
|
group: Group,
|
||||||
account: MangoAccount,
|
account: MangoAccount,
|
||||||
|
tokenCount: number,
|
||||||
|
serum3Count: number,
|
||||||
|
perpCount: number,
|
||||||
|
perpOoCount: number,
|
||||||
): Promise<TransactionSignature> {
|
): Promise<TransactionSignature> {
|
||||||
return await this.program.methods
|
return await this.program.methods
|
||||||
.accountExpand()
|
.accountExpand(tokenCount, serum3Count, perpCount, perpOoCount)
|
||||||
.accounts({
|
.accounts({
|
||||||
group: group.publicKey,
|
group: group.publicKey,
|
||||||
account: account.publicKey,
|
account: account.publicKey,
|
||||||
|
@ -620,7 +616,7 @@ export class MangoClient {
|
||||||
mangoAccount: MangoAccount,
|
mangoAccount: MangoAccount,
|
||||||
tokenName: string,
|
tokenName: string,
|
||||||
amount: number,
|
amount: number,
|
||||||
) {
|
): Promise<TransactionSignature> {
|
||||||
const bank = group.banksMap.get(tokenName)!;
|
const bank = group.banksMap.get(tokenName)!;
|
||||||
|
|
||||||
const tokenAccountPk = await getAssociatedTokenAddress(
|
const tokenAccountPk = await getAssociatedTokenAddress(
|
||||||
|
@ -696,7 +692,7 @@ export class MangoClient {
|
||||||
tokenName: string,
|
tokenName: string,
|
||||||
amount: number,
|
amount: number,
|
||||||
allowBorrow: boolean,
|
allowBorrow: boolean,
|
||||||
) {
|
): Promise<TransactionSignature> {
|
||||||
const bank = group.banksMap.get(tokenName)!;
|
const bank = group.banksMap.get(tokenName)!;
|
||||||
|
|
||||||
const tokenAccountPk = await getAssociatedTokenAddress(
|
const tokenAccountPk = await getAssociatedTokenAddress(
|
||||||
|
@ -737,7 +733,7 @@ export class MangoClient {
|
||||||
tokenName: string,
|
tokenName: string,
|
||||||
nativeAmount: number,
|
nativeAmount: number,
|
||||||
allowBorrow: boolean,
|
allowBorrow: boolean,
|
||||||
) {
|
): Promise<TransactionSignature> {
|
||||||
const bank = group.banksMap.get(tokenName)!;
|
const bank = group.banksMap.get(tokenName)!;
|
||||||
|
|
||||||
const tokenAccountPk = await getAssociatedTokenAddress(
|
const tokenAccountPk = await getAssociatedTokenAddress(
|
||||||
|
@ -1559,6 +1555,28 @@ export class MangoClient {
|
||||||
return this.program.provider.sendAndConfirm(tx);
|
return this.program.provider.sendAndConfirm(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateIndexAndRate(group: Group, tokenName: string) {
|
||||||
|
let bank = group.banksMap.get(tokenName)!;
|
||||||
|
let mintInfo = group.mintInfosMap.get(bank.tokenIndex)!;
|
||||||
|
|
||||||
|
await this.program.methods
|
||||||
|
.tokenUpdateIndexAndRate()
|
||||||
|
.accounts({
|
||||||
|
group: group.publicKey,
|
||||||
|
mintInfo: mintInfo.publicKey,
|
||||||
|
oracle: mintInfo.oracle,
|
||||||
|
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
|
||||||
|
})
|
||||||
|
.remainingAccounts([
|
||||||
|
{
|
||||||
|
pubkey: bank.publicKey,
|
||||||
|
isWritable: true,
|
||||||
|
isSigner: false,
|
||||||
|
} as AccountMeta,
|
||||||
|
])
|
||||||
|
.rpc();
|
||||||
|
}
|
||||||
|
|
||||||
/// liquidations
|
/// liquidations
|
||||||
|
|
||||||
async liqTokenWithToken(
|
async liqTokenWithToken(
|
||||||
|
@ -1688,7 +1706,7 @@ export class MangoClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mintInfos = [...new Set(tokenIndices.sort())].map(
|
const mintInfos = [...new Set(tokenIndices)].map(
|
||||||
(tokenIndex) => group.mintInfosMap.get(tokenIndex)!,
|
(tokenIndex) => group.mintInfosMap.get(tokenIndex)!,
|
||||||
);
|
);
|
||||||
healthRemainingAccounts.push(
|
healthRemainingAccounts.push(
|
||||||
|
|
|
@ -504,6 +504,12 @@ export type MangoV4 = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "groupInsuranceFundOpt",
|
||||||
|
"type": {
|
||||||
|
"option": "bool"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "interestRateParamsOpt",
|
"name": "interestRateParamsOpt",
|
||||||
"type": {
|
"type": {
|
||||||
|
@ -738,6 +744,11 @@ export type MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "tokenUpdateIndexAndRate",
|
"name": "tokenUpdateIndexAndRate",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
|
{
|
||||||
|
"name": "group",
|
||||||
|
"isMut": false,
|
||||||
|
"isSigner": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mintInfo",
|
"name": "mintInfo",
|
||||||
"isMut": false,
|
"isMut": false,
|
||||||
|
@ -815,10 +826,20 @@ export type MangoV4 = {
|
||||||
"type": "u32"
|
"type": "u32"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "accountSize",
|
"name": "tokenCount",
|
||||||
"type": {
|
"type": "u8"
|
||||||
"defined": "AccountSize"
|
},
|
||||||
}
|
{
|
||||||
|
"name": "serum3Count",
|
||||||
|
"type": "u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "perpCount",
|
||||||
|
"type": "u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "perpOoCount",
|
||||||
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "name",
|
"name": "name",
|
||||||
|
@ -855,7 +876,24 @@ export type MangoV4 = {
|
||||||
"isSigner": false
|
"isSigner": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"args": []
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "tokenCount",
|
||||||
|
"type": "u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "serum3Count",
|
||||||
|
"type": "u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "perpCount",
|
||||||
|
"type": "u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "perpOoCount",
|
||||||
|
"type": "u8"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "accountEdit",
|
"name": "accountEdit",
|
||||||
|
@ -2572,6 +2610,11 @@ export type MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "perpUpdateFunding",
|
"name": "perpUpdateFunding",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
|
{
|
||||||
|
"name": "group",
|
||||||
|
"isMut": false,
|
||||||
|
"isSigner": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "perpMarket",
|
"name": "perpMarket",
|
||||||
"isMut": true,
|
"isMut": true,
|
||||||
|
@ -2820,7 +2863,7 @@ export type MangoV4 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "flashLoanVaultInitial",
|
"name": "flashLoanTokenAccountInitial",
|
||||||
"type": "u64"
|
"type": "u64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -3970,6 +4013,21 @@ export type MangoV4 = {
|
||||||
"defined": "I80F48"
|
"defined": "I80F48"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "baseEntryLots",
|
||||||
|
"docs": [
|
||||||
|
"Tracks what the position is to calculate average entry & break even price"
|
||||||
|
],
|
||||||
|
"type": "i64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "quoteEntryNative",
|
||||||
|
"type": "i64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "quoteExitNative",
|
||||||
|
"type": "i64"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "longSettledFunding",
|
"name": "longSettledFunding",
|
||||||
"docs": [
|
"docs": [
|
||||||
|
@ -4277,20 +4335,6 @@ export type MangoV4 = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "AccountSize",
|
|
||||||
"type": {
|
|
||||||
"kind": "enum",
|
|
||||||
"variants": [
|
|
||||||
{
|
|
||||||
"name": "Small"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Large"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "OracleType",
|
"name": "OracleType",
|
||||||
"type": {
|
"type": {
|
||||||
|
@ -4440,6 +4484,11 @@ export type MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "PerpBalanceLog",
|
"name": "PerpBalanceLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoAccount",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
@ -4490,6 +4539,11 @@ export type MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "TokenBalanceLog",
|
"name": "TokenBalanceLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoAccount",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
@ -4523,39 +4577,13 @@ export type MangoV4 = {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "MarginTradeLog",
|
"name": "FlashLoanLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoGroup",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
"index": false
|
"index": false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "tokenIndexes",
|
|
||||||
"type": {
|
|
||||||
"vec": "u16"
|
|
||||||
},
|
|
||||||
"index": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "preIndexedPositions",
|
|
||||||
"type": {
|
|
||||||
"vec": "i128"
|
|
||||||
},
|
|
||||||
"index": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "postIndexedPositions",
|
|
||||||
"type": {
|
|
||||||
"vec": "i128"
|
|
||||||
},
|
|
||||||
"index": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "FlashLoanLog",
|
|
||||||
"fields": [
|
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoAccount",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
@ -4575,6 +4603,11 @@ export type MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "WithdrawLog",
|
"name": "WithdrawLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoAccount",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
@ -4605,6 +4638,11 @@ export type MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "DepositLog",
|
"name": "DepositLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoAccount",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
@ -4830,6 +4868,11 @@ export type MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "LiquidateTokenAndTokenLog",
|
"name": "LiquidateTokenAndTokenLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "liqee",
|
"name": "liqee",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
@ -5508,6 +5551,12 @@ export const IDL: MangoV4 = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "groupInsuranceFundOpt",
|
||||||
|
"type": {
|
||||||
|
"option": "bool"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "interestRateParamsOpt",
|
"name": "interestRateParamsOpt",
|
||||||
"type": {
|
"type": {
|
||||||
|
@ -5742,6 +5791,11 @@ export const IDL: MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "tokenUpdateIndexAndRate",
|
"name": "tokenUpdateIndexAndRate",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
|
{
|
||||||
|
"name": "group",
|
||||||
|
"isMut": false,
|
||||||
|
"isSigner": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mintInfo",
|
"name": "mintInfo",
|
||||||
"isMut": false,
|
"isMut": false,
|
||||||
|
@ -5819,10 +5873,20 @@ export const IDL: MangoV4 = {
|
||||||
"type": "u32"
|
"type": "u32"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "accountSize",
|
"name": "tokenCount",
|
||||||
"type": {
|
"type": "u8"
|
||||||
"defined": "AccountSize"
|
},
|
||||||
}
|
{
|
||||||
|
"name": "serum3Count",
|
||||||
|
"type": "u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "perpCount",
|
||||||
|
"type": "u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "perpOoCount",
|
||||||
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "name",
|
"name": "name",
|
||||||
|
@ -5859,7 +5923,24 @@ export const IDL: MangoV4 = {
|
||||||
"isSigner": false
|
"isSigner": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"args": []
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "tokenCount",
|
||||||
|
"type": "u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "serum3Count",
|
||||||
|
"type": "u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "perpCount",
|
||||||
|
"type": "u8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "perpOoCount",
|
||||||
|
"type": "u8"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "accountEdit",
|
"name": "accountEdit",
|
||||||
|
@ -7576,6 +7657,11 @@ export const IDL: MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "perpUpdateFunding",
|
"name": "perpUpdateFunding",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
|
{
|
||||||
|
"name": "group",
|
||||||
|
"isMut": false,
|
||||||
|
"isSigner": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "perpMarket",
|
"name": "perpMarket",
|
||||||
"isMut": true,
|
"isMut": true,
|
||||||
|
@ -7824,7 +7910,7 @@ export const IDL: MangoV4 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "flashLoanVaultInitial",
|
"name": "flashLoanTokenAccountInitial",
|
||||||
"type": "u64"
|
"type": "u64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -8974,6 +9060,21 @@ export const IDL: MangoV4 = {
|
||||||
"defined": "I80F48"
|
"defined": "I80F48"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "baseEntryLots",
|
||||||
|
"docs": [
|
||||||
|
"Tracks what the position is to calculate average entry & break even price"
|
||||||
|
],
|
||||||
|
"type": "i64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "quoteEntryNative",
|
||||||
|
"type": "i64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "quoteExitNative",
|
||||||
|
"type": "i64"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "longSettledFunding",
|
"name": "longSettledFunding",
|
||||||
"docs": [
|
"docs": [
|
||||||
|
@ -9281,20 +9382,6 @@ export const IDL: MangoV4 = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "AccountSize",
|
|
||||||
"type": {
|
|
||||||
"kind": "enum",
|
|
||||||
"variants": [
|
|
||||||
{
|
|
||||||
"name": "Small"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Large"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "OracleType",
|
"name": "OracleType",
|
||||||
"type": {
|
"type": {
|
||||||
|
@ -9444,6 +9531,11 @@ export const IDL: MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "PerpBalanceLog",
|
"name": "PerpBalanceLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoAccount",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
@ -9494,6 +9586,11 @@ export const IDL: MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "TokenBalanceLog",
|
"name": "TokenBalanceLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoAccount",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
@ -9527,39 +9624,13 @@ export const IDL: MangoV4 = {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "MarginTradeLog",
|
"name": "FlashLoanLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoGroup",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
"index": false
|
"index": false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "tokenIndexes",
|
|
||||||
"type": {
|
|
||||||
"vec": "u16"
|
|
||||||
},
|
|
||||||
"index": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "preIndexedPositions",
|
|
||||||
"type": {
|
|
||||||
"vec": "i128"
|
|
||||||
},
|
|
||||||
"index": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "postIndexedPositions",
|
|
||||||
"type": {
|
|
||||||
"vec": "i128"
|
|
||||||
},
|
|
||||||
"index": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "FlashLoanLog",
|
|
||||||
"fields": [
|
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoAccount",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
@ -9579,6 +9650,11 @@ export const IDL: MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "WithdrawLog",
|
"name": "WithdrawLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoAccount",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
@ -9609,6 +9685,11 @@ export const IDL: MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "DepositLog",
|
"name": "DepositLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mangoAccount",
|
"name": "mangoAccount",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
@ -9834,6 +9915,11 @@ export const IDL: MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "LiquidateTokenAndTokenLog",
|
"name": "LiquidateTokenAndTokenLog",
|
||||||
"fields": [
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "mangoGroup",
|
||||||
|
"type": "publicKey",
|
||||||
|
"index": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "liqee",
|
"name": "liqee",
|
||||||
"type": "publicKey",
|
"type": "publicKey",
|
||||||
|
|
|
@ -200,7 +200,12 @@ async function main() {
|
||||||
console.log(
|
console.log(
|
||||||
`Editing group, setting existing admin as fastListingAdmin to be able to add MNGO truslessly...`,
|
`Editing group, setting existing admin as fastListingAdmin to be able to add MNGO truslessly...`,
|
||||||
);
|
);
|
||||||
await client.groupEdit(group, group.admin, group.admin);
|
let sig = await client.groupEdit(
|
||||||
|
group,
|
||||||
|
group.admin,
|
||||||
|
new PublicKey('Efhak3qj3MiyzgJr3cUUqXXz5wr3oYHt9sPzuqJf9eBN'),
|
||||||
|
);
|
||||||
|
console.log(`sig https://explorer.solana.com/tx/${sig}?cluster=devnet`);
|
||||||
console.log(`Registering MNGO...`);
|
console.log(`Registering MNGO...`);
|
||||||
const mngoDevnetMint = new PublicKey(DEVNET_MINTS.get('MNGO')!);
|
const mngoDevnetMint = new PublicKey(DEVNET_MINTS.get('MNGO')!);
|
||||||
const mngoDevnetOracle = new PublicKey(DEVNET_ORACLES.get('MNGO')!);
|
const mngoDevnetOracle = new PublicKey(DEVNET_ORACLES.get('MNGO')!);
|
||||||
|
@ -296,6 +301,7 @@ async function main() {
|
||||||
'USDC',
|
'USDC',
|
||||||
btcDevnetOracle,
|
btcDevnetOracle,
|
||||||
0.1,
|
0.1,
|
||||||
|
undefined,
|
||||||
0.01,
|
0.01,
|
||||||
0.3,
|
0.3,
|
||||||
0.08,
|
0.08,
|
||||||
|
@ -323,6 +329,7 @@ async function main() {
|
||||||
'USDC',
|
'USDC',
|
||||||
usdcDevnetOracle.publicKey,
|
usdcDevnetOracle.publicKey,
|
||||||
0.1,
|
0.1,
|
||||||
|
undefined,
|
||||||
0.01,
|
0.01,
|
||||||
0.4,
|
0.4,
|
||||||
0.07,
|
0.07,
|
||||||
|
|
|
@ -107,9 +107,8 @@ async function main() {
|
||||||
group,
|
group,
|
||||||
mangoAccount,
|
mangoAccount,
|
||||||
group.findBank(token.tokenIndex)!.name,
|
group.findBank(token.tokenIndex)!.name,
|
||||||
nativeFlooredNumber,
|
nativeFlooredNumber - 1 /* see comment in token_withdraw in program */,
|
||||||
false,
|
false,
|
||||||
user,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,14 @@ async function main() {
|
||||||
console.log(mangoAccount.toString());
|
console.log(mangoAccount.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
console.log(
|
||||||
|
`...expanding mango account to have serum3 and perp position slots`,
|
||||||
|
);
|
||||||
|
await client.expandMangoAccount(group, mangoAccount, 16, 8, 8, 8);
|
||||||
|
await mangoAccount.reload(client, group);
|
||||||
|
}
|
||||||
|
|
||||||
if (true) {
|
if (true) {
|
||||||
// deposit and withdraw
|
// deposit and withdraw
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue