Merge branch 'dev'

--wip-- [skip ci]
This commit is contained in:
microwavedcola1 2022-08-13 19:55:37 +02:00
commit 5221bbd53b
36 changed files with 546 additions and 414 deletions

12
Cargo.lock generated
View File

@ -1577,6 +1577,17 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0"
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2 1.0.39",
"quote 1.0.18",
"syn 1.0.95",
]
[[package]]
name = "derive_more"
version = "0.99.17"
@ -3192,6 +3203,7 @@ dependencies = [
"borsh",
"bytemuck",
"checked_math",
"derivative",
"env_logger 0.9.0",
"fixed",
"fixed-macro",

View File

@ -16,6 +16,6 @@ RUN --mount=type=cache,target=/usr/local/cargo,from=rust,source=/usr/local/cargo
# Copy bins out of cache
RUN --mount=type=cache,target=target mkdir .bin && cp target/release/keeper target/release/liquidator .bin/
FROM debian:buster-slim as run
RUN apt-get update && apt-get -y install ca-certificates
FROM debian:bullseye-slim as run
RUN apt-get update && apt-get -y install ca-certificates libc6
COPY --from=build /app/.bin/* /usr/local/bin/

View File

@ -2,4 +2,4 @@
# heroku container:push keeper -R -a HEROKU_APP_NAME
# heroku container:release -a HEROKU_APP_NAME
FROM us-docker.pkg.dev/mango-markets/gcr.io/mango-v4:latest
ENTRYPOINT ["liquidator"]
CMD ["liquidator"]

View File

@ -28,6 +28,7 @@ bincode = "1.3.3"
borsh = { version = "0.9.3", features = ["const-generics"] }
bytemuck = "^1.7.2"
checked_math = { path = "../../lib/checked_math" }
derivative = "2.2.0"
fixed = { version = "=1.11.0", features = ["serde", "borsh"] } # todo: higher versions don't work
fixed-macro = "^1.1.1"
mango-macro = { path = "../../mango-macro" }

View File

@ -27,22 +27,19 @@ pub struct AccountClose<'info> {
pub fn account_close(ctx: Context<AccountClose>) -> Result<()> {
let group = ctx.accounts.group.load()?;
{
let account = ctx.accounts.account.load_mut()?;
let account = ctx.accounts.account.load_mut()?;
// don't perform checks if group is just testing
if group.testing == 0 {
require!(!account.fixed.being_liquidated(), MangoError::SomeError);
require_eq!(account.fixed.delegate, Pubkey::default());
for ele in account.token_iter() {
require_eq!(ele.is_active(), false);
}
for ele in account.serum3_iter() {
require_eq!(ele.is_active(), false);
}
for ele in account.perp_iter() {
require_eq!(ele.is_active(), false);
}
// don't perform checks if group is just testing
if !group.is_testing() {
require!(!account.fixed.being_liquidated(), MangoError::SomeError);
for ele in account.token_iter() {
require_eq!(ele.is_active(), false);
}
for ele in account.serum3_iter() {
require_eq!(ele.is_active(), false);
}
for ele in account.perp_iter() {
require_eq!(ele.is_active(), false);
}
}

View File

@ -2,7 +2,7 @@ use anchor_lang::prelude::*;
use crate::error::*;
use crate::state::*;
use crate::util::fill32_from_str;
use crate::util::fill_from_str;
#[derive(Accounts)]
#[instruction(account_num: u32, token_count: u8, serum3_count: u8, perp_count: u8, perp_oo_count: u8)]
@ -11,7 +11,7 @@ pub struct AccountCreate<'info> {
#[account(
init,
seeds = [group.key().as_ref(), b"MangoAccount".as_ref(), owner.key().as_ref(), &account_num.to_le_bytes()],
seeds = [b"MangoAccount".as_ref(), group.key().as_ref(), owner.key().as_ref(), &account_num.to_le_bytes()],
bump,
payer = payer,
space = MangoAccount::space(token_count, serum3_count, perp_count, perp_oo_count)?,
@ -41,7 +41,7 @@ pub fn account_create(
account.header_version()
);
account.fixed.name = fill32_from_str(name)?;
account.fixed.name = fill_from_str(&name)?;
account.fixed.group = ctx.accounts.group.key();
account.fixed.owner = ctx.accounts.owner.key();
account.fixed.account_num = account_num;

View File

@ -1,8 +1,8 @@
use crate::error::MangoError;
use anchor_lang::prelude::*;
use crate::error::MangoError;
use crate::state::*;
use crate::util::fill32_from_str;
use crate::util::fill_from_str;
#[derive(Accounts)]
pub struct AccountEdit<'info> {
@ -34,7 +34,7 @@ pub fn account_edit(
// please maintain, and don't remove, makes it easy to reason about which support modification by owner
if let Some(name) = name_opt {
account.fixed.name = fill32_from_str(name)?;
account.fixed.name = fill_from_str(&name)?;
}
// unchanged -

View File

@ -1,6 +1,7 @@
use anchor_lang::prelude::*;
use crate::state::*;
use crate::util::checked_math as cm;
#[derive(Accounts)]
pub struct AccountExpand<'info> {
@ -32,6 +33,7 @@ pub fn account_expand(
let realloc_account = ctx.accounts.account.as_ref();
let old_space = realloc_account.data_len();
let old_lamports = realloc_account.lamports();
require_gt!(new_space, old_space);
@ -44,13 +46,11 @@ pub fn account_expand(
to: realloc_account.clone(),
},
),
new_rent_minimum
.checked_sub(realloc_account.lamports())
.unwrap(),
cm!(new_rent_minimum - old_lamports),
)?;
// realloc
realloc_account.realloc(new_space, true)?;
// realloc: it's safe to not re-zero-init since we never shrink accounts
realloc_account.realloc(new_space, false)?;
// 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()?;

View File

@ -26,9 +26,7 @@ pub struct GroupClose<'info> {
}
pub fn group_close(ctx: Context<GroupClose>) -> Result<()> {
// TODO: checks
// close insurance vault
// close insurance vault (must be empty)
let group = ctx.accounts.group.load()?;
let group_seeds = group_seeds!(group);
let cpi_accounts = CloseAccount {

View File

@ -22,7 +22,7 @@ pub struct GroupCreate<'info> {
#[account(
init,
seeds = [group.key().as_ref(), b"InsuranceVault".as_ref()],
seeds = [b"InsuranceVault".as_ref(), group.key().as_ref()],
bump,
token::authority = group,
token::mint = insurance_mint,
@ -46,12 +46,12 @@ pub fn group_create(
) -> Result<()> {
let mut group = ctx.accounts.group.load_init()?;
group.creator = ctx.accounts.creator.key();
group.group_num = group_num;
group.admin = ctx.accounts.creator.key();
group.fast_listing_admin = Pubkey::default();
group.insurance_vault = ctx.accounts.insurance_vault.key();
group.insurance_mint = ctx.accounts.insurance_mint.key();
group.bump = *ctx.bumps.get("group").ok_or(MangoError::SomeError)?;
group.group_num = group_num;
group.testing = testing;
group.version = version;
Ok(())

View File

@ -13,14 +13,31 @@ pub struct GroupEdit<'info> {
}
// use case - transfer group ownership to governance, where
// new_admin and new_fast_listing_admin are PDAs
// admin and fast_listing_admin are PDAs
pub fn group_edit(
ctx: Context<GroupEdit>,
new_admin: Pubkey,
new_fast_listing_admin: Pubkey,
admin_opt: Option<Pubkey>,
fast_listing_admin_opt: Option<Pubkey>,
testing_opt: Option<u8>,
version_opt: Option<u8>,
) -> Result<()> {
let mut group = ctx.accounts.group.load_mut()?;
group.admin = new_admin;
group.fast_listing_admin = new_fast_listing_admin;
if let Some(admin) = admin_opt {
group.admin = admin;
}
if let Some(fast_listing_admin) = fast_listing_admin_opt {
group.fast_listing_admin = fast_listing_admin;
}
if let Some(testing) = testing_opt {
group.testing = testing;
}
if let Some(version) = version_opt {
group.version = version;
}
Ok(())
}

View File

@ -4,7 +4,7 @@ use fixed::types::I80F48;
use crate::error::MangoError;
use crate::state::*;
use crate::util::fill16_from_str;
use crate::util::fill_from_str;
#[derive(Accounts)]
#[instruction(perp_market_index: PerpMarketIndex)]
@ -21,7 +21,7 @@ pub struct PerpCreateMarket<'info> {
#[account(
init,
seeds = [group.key().as_ref(), b"PerpMarket".as_ref(), perp_market_index.to_le_bytes().as_ref()],
seeds = [b"PerpMarket".as_ref(), group.key().as_ref(), perp_market_index.to_le_bytes().as_ref()],
bump,
payer = payer,
space = 8 + std::mem::size_of::<PerpMarket>(),
@ -66,7 +66,7 @@ pub fn perp_create_market(
) -> Result<()> {
let mut perp_market = ctx.accounts.perp_market.load_init()?;
*perp_market = PerpMarket {
name: fill16_from_str(name)?,
name: fill_from_str(&name)?,
group: ctx.accounts.group.key(),
oracle: ctx.accounts.oracle.key(),
oracle_config,

View File

@ -25,7 +25,7 @@ pub struct Serum3CreateOpenOrders<'info> {
// initialized by this instruction via cpi to serum
#[account(
init,
seeds = [account.key().as_ref(), b"Serum3OO".as_ref(), serum_market.key().as_ref()],
seeds = [b"Serum3OO".as_ref(), account.key().as_ref(), serum_market.key().as_ref()],
bump,
payer = payer,
owner = serum_program.key(),

View File

@ -3,7 +3,7 @@ use anchor_lang::prelude::*;
use crate::error::MangoError;
use crate::serum3_cpi::{load_market_state, pubkey_from_u64_array};
use crate::state::*;
use crate::util::fill16_from_str;
use crate::util::fill_from_str;
#[derive(Accounts)]
pub struct Serum3RegisterMarket<'info> {
@ -24,7 +24,7 @@ pub struct Serum3RegisterMarket<'info> {
#[account(
init,
// using the serum_market_external in the seed guards against registering the same market twice
seeds = [group.key().as_ref(), b"Serum3Market".as_ref(), serum_market_external.key().as_ref()],
seeds = [b"Serum3Market".as_ref(), group.key().as_ref(), serum_market_external.key().as_ref()],
bump,
payer = payer,
space = 8 + std::mem::size_of::<Serum3Market>(),
@ -67,7 +67,7 @@ pub fn serum3_register_market(
let mut serum_market = ctx.accounts.serum_market.load_init()?;
*serum_market = Serum3Market {
name: fill16_from_str(name)?,
name: fill_from_str(&name)?,
group: ctx.accounts.group.key(),
serum_program: ctx.accounts.serum_program.key(),
serum_market_external: ctx.accounts.serum_market_external.key(),

View File

@ -13,7 +13,7 @@ pub struct StubOracleCreate<'info> {
#[account(
init,
seeds = [group.key().as_ref(), b"StubOracle".as_ref(), mint.key().as_ref()],
seeds = [b"StubOracle".as_ref(), group.key().as_ref(), mint.key().as_ref()],
bump,
payer = payer,
space = 8 + std::mem::size_of::<StubOracle>(),

View File

@ -1,6 +1,7 @@
use anchor_lang::prelude::*;
use anchor_spl::token::{Mint, Token, TokenAccount};
use crate::error::*;
use crate::state::*;
#[derive(Accounts)]
@ -25,7 +26,7 @@ pub struct TokenAddBank<'info> {
#[account(
init,
// using the token_index in this seed guards against reusing it
seeds = [group.key().as_ref(), b"Bank".as_ref(), &token_index.to_le_bytes(), &bank_num.to_le_bytes()],
seeds = [b"Bank".as_ref(), group.key().as_ref(), &token_index.to_le_bytes(), &bank_num.to_le_bytes()],
bump,
payer = payer,
space = 8 + std::mem::size_of::<Bank>(),
@ -34,7 +35,7 @@ pub struct TokenAddBank<'info> {
#[account(
init,
seeds = [group.key().as_ref(), b"Vault".as_ref(), &token_index.to_le_bytes(), &bank_num.to_le_bytes()],
seeds = [b"Vault".as_ref(), group.key().as_ref(), &token_index.to_le_bytes(), &bank_num.to_le_bytes()],
bump,
token::authority = group,
token::mint = mint,
@ -44,10 +45,12 @@ pub struct TokenAddBank<'info> {
#[account(
mut,
seeds = [group.key().as_ref(), b"MintInfo".as_ref(), mint.key().as_ref()],
bump
constraint = mint_info.load()?.token_index == token_index,
has_one = group,
has_one = mint,
)]
pub mint_info: AccountLoader<'info, MintInfo>,
#[account(mut)]
pub payer: Signer<'info>,
@ -56,8 +59,6 @@ pub struct TokenAddBank<'info> {
pub rent: Sysvar<'info, Rent>,
}
// TODO: should this be "configure_mint", we pass an explicit index, and allow
// overwriting config as long as the mint account stays the same?
#[allow(clippy::too_many_arguments)]
#[allow(unused_variables)]
pub fn token_add_bank(
@ -65,11 +66,10 @@ pub fn token_add_bank(
token_index: TokenIndex,
bank_num: u32,
) -> Result<()> {
// TODO: Error if mint is already configured (technically, init of vault will fail)
let existing_bank = ctx.accounts.existing_bank.load()?;
let mut bank = ctx.accounts.bank.load_init()?;
*bank = Bank::from_existing_bank(&existing_bank, ctx.accounts.vault.key(), bank_num);
let bump = *ctx.bumps.get("bank").ok_or(MangoError::SomeError)?;
*bank = Bank::from_existing_bank(&existing_bank, ctx.accounts.vault.key(), bank_num, bump);
let mut mint_info = ctx.accounts.mint_info.load_mut()?;
let free_slot = mint_info

View File

@ -48,9 +48,6 @@ impl<'info> TokenDeposit<'info> {
}
}
// TODO: It may make sense to have the token_index passed in from the outside.
// That would save a lot of computation that needs to go into finding the
// right index for the mint.
pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
require_msg!(amount > 0, "deposit amount must be positive");

View File

@ -7,6 +7,10 @@ use crate::accounts_zerocopy::LoadMutZeroCopyRef;
use crate::state::*;
/// Changes a token's parameters.
///
/// In addition to these accounts, all banks must be passed as remaining_accounts
/// in MintInfo order.
#[derive(Accounts)]
#[instruction(bank_num: u64)]
pub struct TokenEdit<'info> {

View File

@ -5,7 +5,7 @@ use fixed_macro::types::I80F48;
use crate::error::*;
use crate::state::*;
use crate::util::fill16_from_str;
use crate::util::fill_from_str;
pub const INDEX_START: I80F48 = I80F48!(1_000_000);
@ -25,7 +25,7 @@ pub struct TokenRegister<'info> {
#[account(
init,
// using the token_index in this seed guards against reusing it
seeds = [group.key().as_ref(), b"Bank".as_ref(), &token_index.to_le_bytes(), &FIRST_BANK_NUM.to_le_bytes()],
seeds = [b"Bank".as_ref(), group.key().as_ref(), &token_index.to_le_bytes(), &FIRST_BANK_NUM.to_le_bytes()],
bump,
payer = payer,
space = 8 + std::mem::size_of::<Bank>(),
@ -34,7 +34,7 @@ pub struct TokenRegister<'info> {
#[account(
init,
seeds = [group.key().as_ref(), b"Vault".as_ref(), &token_index.to_le_bytes(), &FIRST_BANK_NUM.to_le_bytes()],
seeds = [b"Vault".as_ref(), group.key().as_ref(), &token_index.to_le_bytes(), &FIRST_BANK_NUM.to_le_bytes()],
bump,
token::authority = group,
token::mint = mint,
@ -45,7 +45,7 @@ pub struct TokenRegister<'info> {
#[account(
init,
// using the mint in this seed guards against registering the same mint twice
seeds = [group.key().as_ref(), b"MintInfo".as_ref(), mint.key().as_ref()],
seeds = [b"MintInfo".as_ref(), group.key().as_ref(), mint.key().as_ref()],
bump,
payer = payer,
space = 8 + std::mem::size_of::<MintInfo>(),
@ -73,8 +73,6 @@ pub struct InterestRateParams {
pub adjustment_factor: f32,
}
// TODO: should this be "configure_mint", we pass an explicit index, and allow
// overwriting config as long as the mint account stays the same?
#[allow(clippy::too_many_arguments)]
pub fn token_register(
ctx: Context<TokenRegister>,
@ -101,7 +99,7 @@ pub fn token_register(
let mut bank = ctx.accounts.bank.load_init()?;
*bank = Bank {
group: ctx.accounts.group.key(),
name: fill16_from_str(name)?,
name: fill_from_str(&name)?,
mint: ctx.accounts.mint.key(),
vault: ctx.accounts.vault.key(),
oracle: ctx.accounts.oracle.key(),

View File

@ -5,7 +5,7 @@ use fixed::types::I80F48;
use crate::error::*;
use crate::instructions::INDEX_START;
use crate::state::*;
use crate::util::fill16_from_str;
use crate::util::fill_from_str;
const FIRST_BANK_NUM: u32 = 0;
@ -23,7 +23,7 @@ pub struct TokenRegisterTrustless<'info> {
#[account(
init,
// using the token_index in this seed guards against reusing it
seeds = [group.key().as_ref(), b"Bank".as_ref(), &token_index.to_le_bytes(), &FIRST_BANK_NUM.to_le_bytes()],
seeds = [b"Bank".as_ref(), group.key().as_ref(), &token_index.to_le_bytes(), &FIRST_BANK_NUM.to_le_bytes()],
bump,
payer = payer,
space = 8 + std::mem::size_of::<Bank>(),
@ -32,7 +32,7 @@ pub struct TokenRegisterTrustless<'info> {
#[account(
init,
seeds = [group.key().as_ref(), b"Vault".as_ref(), &token_index.to_le_bytes(), &FIRST_BANK_NUM.to_le_bytes()],
seeds = [b"Vault".as_ref(), group.key().as_ref(), &token_index.to_le_bytes(), &FIRST_BANK_NUM.to_le_bytes()],
bump,
token::authority = group,
token::mint = mint,
@ -43,7 +43,7 @@ pub struct TokenRegisterTrustless<'info> {
#[account(
init,
// using the mint in this seed guards against registering the same mint twice
seeds = [group.key().as_ref(), b"MintInfo".as_ref(), mint.key().as_ref()],
seeds = [b"MintInfo".as_ref(), group.key().as_ref(), mint.key().as_ref()],
bump,
payer = payer,
space = 8 + std::mem::size_of::<MintInfo>(),
@ -72,7 +72,7 @@ pub fn token_register_trustless(
let mut bank = ctx.accounts.bank.load_init()?;
*bank = Bank {
group: ctx.accounts.group.key(),
name: fill16_from_str(name)?,
name: fill_from_str(&name)?,
mint: ctx.accounts.mint.key(),
vault: ctx.accounts.vault.key(),
oracle: ctx.accounts.oracle.key(),

View File

@ -17,6 +17,13 @@ pub mod compute_budget {
declare_id!("ComputeBudget111111111111111111111111111111");
}
/// Updates token interest and interest rates.
///
/// In addition to these accounts, all banks must be passed as remaining_accounts
/// in MintInfo order.
///
/// This instruction may only be used alongside other instructions of the same kind
/// or ComputeBudget instructions.
#[derive(Accounts)]
pub struct TokenUpdateIndexAndRate<'info> {
pub group: AccountLoader<'info, Group>, // Required for group metadata parsing

View File

@ -41,10 +41,18 @@ pub mod mango_v4 {
pub fn group_edit(
ctx: Context<GroupEdit>,
new_admin: Pubkey,
new_fast_listing_admin: Pubkey,
admin_opt: Option<Pubkey>,
fast_listing_admin_opt: Option<Pubkey>,
testing_opt: Option<u8>,
version_opt: Option<u8>,
) -> Result<()> {
instructions::group_edit(ctx, new_admin, new_fast_listing_admin)
instructions::group_edit(
ctx,
admin_opt,
fast_listing_admin_opt,
testing_opt,
version_opt,
)
}
pub fn group_close(ctx: Context<GroupClose>) -> Result<()> {

View File

@ -1,6 +1,8 @@
use super::{OracleConfig, TokenIndex, TokenPosition};
use crate::util;
use crate::util::checked_math as cm;
use anchor_lang::prelude::*;
use derivative::Derivative;
use fixed::types::I80F48;
use fixed_macro::types::I80F48;
use static_assertions::const_assert_eq;
@ -13,11 +15,14 @@ pub const DAY_I80F48: I80F48 = I80F48!(86400);
pub const YEAR_I80F48: I80F48 = I80F48!(31536000);
pub const MINIMUM_MAX_RATE: I80F48 = I80F48!(0.5);
#[derive(Derivative)]
#[derivative(Debug)]
#[account(zero_copy)]
pub struct Bank {
// ABI: Clients rely on this being at offset 8
pub group: Pubkey,
#[derivative(Debug(format_with = "util::format_zero_terminated_utf8_bytes"))]
pub name: [u8; 16],
pub mint: Pubkey,
@ -94,6 +99,7 @@ pub struct Bank {
pub bank_num: u32,
#[derivative(Debug = "ignore")]
pub reserved: [u8; 2560],
}
const_assert_eq!(
@ -102,73 +108,37 @@ const_assert_eq!(
);
const_assert_eq!(size_of::<Bank>() % 8, 0);
impl std::fmt::Debug for Bank {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Bank")
.field("name", &self.name())
.field("group", &self.group)
.field("mint", &self.mint)
.field("vault", &self.vault)
.field("oracle", &self.oracle)
.field("oracle_config", &self.oracle_config)
.field("deposit_index", &self.deposit_index)
.field("borrow_index", &self.borrow_index)
.field(
"cached_indexed_total_deposits",
&self.cached_indexed_total_deposits,
)
.field(
"cached_indexed_total_borrows",
&self.cached_indexed_total_borrows,
)
.field("indexed_deposits", &self.indexed_deposits)
.field("indexed_borrows", &self.indexed_borrows)
.field("index_last_updated", &self.index_last_updated)
.field("bank_rate_last_updated", &self.bank_rate_last_updated)
.field("avg_utilization", &self.avg_utilization)
.field("util0", &self.util0)
.field("rate0", &self.rate0)
.field("util1", &self.util1)
.field("rate1", &self.rate1)
.field("max_rate", &self.max_rate)
.field("collected_fees_native", &self.collected_fees_native)
.field("loan_origination_fee_rate", &self.loan_origination_fee_rate)
.field("loan_fee_rate", &self.loan_fee_rate)
.field("maint_asset_weight", &self.maint_asset_weight)
.field("init_asset_weight", &self.init_asset_weight)
.field("maint_liab_weight", &self.maint_liab_weight)
.field("init_liab_weight", &self.init_liab_weight)
.field("liquidation_fee", &self.liquidation_fee)
.field("dust", &self.dust)
.field("token_index", &self.token_index)
.field(
"flash_loan_approved_amount",
&self.flash_loan_approved_amount,
)
.field(
"flash_loan_token_account_initial",
&self.flash_loan_token_account_initial,
)
.field("reserved", &self.reserved)
.finish()
}
}
impl Bank {
pub fn from_existing_bank(existing_bank: &Bank, vault: Pubkey, bank_num: u32) -> Self {
pub fn from_existing_bank(
existing_bank: &Bank,
vault: Pubkey,
bank_num: u32,
bump: u8,
) -> Self {
Self {
// values that must be reset/changed
vault,
indexed_deposits: I80F48::ZERO,
indexed_borrows: I80F48::ZERO,
collected_fees_native: I80F48::ZERO,
dust: I80F48::ZERO,
flash_loan_approved_amount: 0,
flash_loan_token_account_initial: u64::MAX,
bump,
bank_num,
// values that can be copied
// these are listed explicitly, so someone must make the decision when a
// new field is added!
name: existing_bank.name,
group: existing_bank.group,
mint: existing_bank.mint,
vault,
oracle: existing_bank.oracle,
oracle_config: existing_bank.oracle_config,
deposit_index: existing_bank.deposit_index,
borrow_index: existing_bank.borrow_index,
cached_indexed_total_deposits: existing_bank.cached_indexed_total_deposits,
cached_indexed_total_borrows: existing_bank.cached_indexed_total_borrows,
indexed_deposits: I80F48::ZERO,
indexed_borrows: I80F48::ZERO,
index_last_updated: existing_bank.index_last_updated,
bank_rate_last_updated: existing_bank.bank_rate_last_updated,
avg_utilization: existing_bank.avg_utilization,
@ -178,7 +148,6 @@ impl Bank {
util1: existing_bank.util1,
rate1: existing_bank.rate1,
max_rate: existing_bank.max_rate,
collected_fees_native: existing_bank.collected_fees_native,
loan_origination_fee_rate: existing_bank.loan_origination_fee_rate,
loan_fee_rate: existing_bank.loan_fee_rate,
maint_asset_weight: existing_bank.maint_asset_weight,
@ -186,14 +155,9 @@ impl Bank {
maint_liab_weight: existing_bank.maint_liab_weight,
init_liab_weight: existing_bank.init_liab_weight,
liquidation_fee: existing_bank.liquidation_fee,
dust: I80F48::ZERO,
flash_loan_approved_amount: 0,
flash_loan_token_account_initial: u64::MAX,
token_index: existing_bank.token_index,
bump: existing_bank.bump,
mint_decimals: existing_bank.mint_decimals,
reserved: [0; 2560],
bank_num,
}
}
@ -535,8 +499,8 @@ impl Bank {
macro_rules! bank_seeds {
( $bank:expr ) => {
&[
$bank.group.as_ref(),
b"Bank".as_ref(),
$bank.group.as_ref(),
$bank.token_index.to_le_bytes(),
&bank.bank_num.to_le_bytes(),
&[$bank.bump],

View File

@ -956,14 +956,10 @@ impl<
}
}
// update header
let header_mut = self.header_mut();
header_mut.token_count = new_token_count;
header_mut.serum3_count = new_serum3_count;
header_mut.perp_count = new_perp_count;
header_mut.perp_oo_count = new_perp_oo_count;
// update the already-parsed header
*self.header_mut() = new_header;
// write new lengths (uses header)
// write new lengths to the dynamic data (uses header)
self.write_token_length();
self.write_serum3_length();
self.write_perp_length();

View File

@ -1,5 +1,6 @@
use anchor_lang::prelude::*;
use checked_math as cm;
use derivative::Derivative;
use fixed::types::I80F48;
use static_assertions::const_assert_eq;
use std::cmp::Ordering;
@ -10,7 +11,8 @@ use crate::state::*;
pub const FREE_ORDER_SLOT: PerpMarketIndex = PerpMarketIndex::MAX;
#[zero_copy]
#[derive(AnchorDeserialize, AnchorSerialize, Debug)]
#[derive(AnchorDeserialize, AnchorSerialize, Derivative)]
#[derivative(Debug)]
pub struct TokenPosition {
// TODO: Why did we have deposits and borrows as two different values
// if only one of them was allowed to be != 0 at a time?
@ -26,8 +28,10 @@ pub struct TokenPosition {
/// incremented when a market requires this position to stay alive
pub in_use_count: u8,
#[derivative(Debug = "ignore")]
pub padding: [u8; 5],
#[derivative(Debug = "ignore")]
pub reserved: [u8; 40],
}
@ -82,7 +86,8 @@ impl TokenPosition {
}
#[zero_copy]
#[derive(AnchorSerialize, AnchorDeserialize, Debug)]
#[derive(AnchorSerialize, AnchorDeserialize, Derivative)]
#[derivative(Debug)]
pub struct Serum3Orders {
pub open_orders: Pubkey,
@ -101,8 +106,10 @@ pub struct Serum3Orders {
pub base_token_index: TokenIndex,
pub quote_token_index: TokenIndex,
#[derivative(Debug = "ignore")]
pub padding: [u8; 2],
#[derivative(Debug = "ignore")]
pub reserved: [u8; 64],
}
const_assert_eq!(size_of::<Serum3Orders>(), 32 + 8 * 2 + 2 * 3 + 2 + 64);
@ -137,9 +144,11 @@ impl Default for Serum3Orders {
}
#[zero_copy]
#[derive(AnchorSerialize, AnchorDeserialize)]
#[derive(AnchorSerialize, AnchorDeserialize, Derivative)]
#[derivative(Debug)]
pub struct PerpPositions {
pub market_index: PerpMarketIndex,
#[derivative(Debug = "ignore")]
pub padding: [u8; 6],
/// Active position size, measured in base lots
@ -168,22 +177,9 @@ pub struct PerpPositions {
pub taker_base_lots: i64,
pub taker_quote_lots: i64,
#[derivative(Debug = "ignore")]
pub reserved: [u8; 64],
}
impl std::fmt::Debug for PerpPositions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PerpAccount")
.field("market_index", &self.market_index)
.field("base_position_lots", &self.base_position_lots)
.field("quote_position_native", &self.quote_position_native)
.field("bids_base_lots", &self.bids_base_lots)
.field("asks_base_lots", &self.asks_base_lots)
.field("taker_base_lots", &self.taker_base_lots)
.field("taker_quote_lots", &self.taker_quote_lots)
.finish()
}
}
const_assert_eq!(size_of::<PerpPositions>(), 8 + 7 * 8 + 3 * 16 + 64);
const_assert_eq!(size_of::<PerpPositions>() % 8, 0);
@ -358,8 +354,8 @@ const_assert_eq!(size_of::<PerpOpenOrders>() % 8, 0);
macro_rules! account_seeds {
( $account:expr ) => {
&[
$account.group.as_ref(),
b"MangoAccount".as_ref(),
$account.group.as_ref(),
$account.owner.as_ref(),
&$account.account_num.to_le_bytes(),
&[$account.bump],

View File

@ -48,8 +48,8 @@ impl Serum3Market {
macro_rules! serum_market_seeds {
( $acc:expr ) => {
&[
$acc.group.as_ref(),
b"Serum3Market".as_ref(),
$acc.group.as_ref(),
$acc.serum_market_external.as_ref(),
&[$acc.bump],
]

View File

@ -20,18 +20,22 @@ macro_rules! checked_math {
}
pub(crate) use checked_math;
pub fn fill16_from_str(name: String) -> Result<[u8; 16]> {
pub fn fill_from_str<const N: usize>(name: &str) -> Result<[u8; N]> {
let name_bytes = name.as_bytes();
require!(name_bytes.len() < 16, MangoError::SomeError);
let mut name_ = [0u8; 16];
require!(name_bytes.len() < N, MangoError::SomeError);
let mut name_ = [0u8; N];
name_[..name_bytes.len()].copy_from_slice(name_bytes);
Ok(name_)
}
pub fn fill32_from_str(name: String) -> Result<[u8; 32]> {
let name_bytes = name.as_bytes();
require!(name_bytes.len() < 32, MangoError::SomeError);
let mut name_ = [0u8; 32];
name_[..name_bytes.len()].copy_from_slice(name_bytes);
Ok(name_)
pub fn format_zero_terminated_utf8_bytes(
name: &[u8],
fmt: &mut std::fmt::Formatter,
) -> std::result::Result<(), std::fmt::Error> {
fmt.write_str(
std::str::from_utf8(name)
.unwrap()
.trim_matches(char::from(0)),
)
.into()
}

View File

@ -128,8 +128,8 @@ async fn get_mint_info_by_mint(
) -> MintInfo {
let mint_info_pk = Pubkey::find_program_address(
&[
account.fixed.group.as_ref(),
b"MintInfo".as_ref(),
account.fixed.group.as_ref(),
mint.as_ref(),
],
&mango_v4::id(),
@ -145,8 +145,8 @@ async fn get_mint_info_by_token_index(
) -> MintInfo {
let bank_pk = Pubkey::find_program_address(
&[
account.fixed.group.as_ref(),
b"Bank".as_ref(),
account.fixed.group.as_ref(),
&token_index.to_le_bytes(),
&0u32.to_le_bytes(),
],
@ -160,8 +160,8 @@ async fn get_mint_info_by_token_index(
fn get_perp_market_address_by_index(group: Pubkey, perp_market_index: PerpMarketIndex) -> Pubkey {
Pubkey::find_program_address(
&[
group.as_ref(),
b"PerpMarket".as_ref(),
group.as_ref(),
&perp_market_index.to_le_bytes(),
],
&mango_v4::id(),
@ -466,8 +466,8 @@ impl<'keypair> ClientInstruction for TokenWithdrawInstruction<'keypair> {
.unwrap();
let mint_info = Pubkey::find_program_address(
&[
account.fixed.group.as_ref(),
b"MintInfo".as_ref(),
account.fixed.group.as_ref(),
token_account.mint.as_ref(),
],
&program_id,
@ -534,8 +534,8 @@ impl ClientInstruction for TokenDepositInstruction {
.unwrap();
let mint_info = Pubkey::find_program_address(
&[
account.fixed.group.as_ref(),
b"MintInfo".as_ref(),
account.fixed.group.as_ref(),
token_account.mint.as_ref(),
],
&program_id,
@ -633,8 +633,8 @@ impl<'keypair> ClientInstruction for TokenRegisterInstruction<'keypair> {
let bank = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"Bank".as_ref(),
self.group.as_ref(),
&self.token_index.to_le_bytes(),
&0u32.to_le_bytes(),
],
@ -643,8 +643,8 @@ impl<'keypair> ClientInstruction for TokenRegisterInstruction<'keypair> {
.0;
let vault = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"Vault".as_ref(),
self.group.as_ref(),
&self.token_index.to_le_bytes(),
&0u32.to_le_bytes(),
],
@ -653,8 +653,8 @@ impl<'keypair> ClientInstruction for TokenRegisterInstruction<'keypair> {
.0;
let mint_info = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"MintInfo".as_ref(),
self.group.as_ref(),
self.mint.as_ref(),
],
&program_id,
@ -663,8 +663,8 @@ impl<'keypair> ClientInstruction for TokenRegisterInstruction<'keypair> {
// TODO: remove copy pasta of pda derivation, use reference
let oracle = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"StubOracle".as_ref(),
self.group.as_ref(),
self.mint.as_ref(),
],
&program_id,
@ -718,8 +718,8 @@ impl<'keypair> ClientInstruction for TokenAddBankInstruction<'keypair> {
let existing_bank = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"Bank".as_ref(),
self.group.as_ref(),
&self.token_index.to_le_bytes(),
&0u32.to_le_bytes(),
],
@ -728,8 +728,8 @@ impl<'keypair> ClientInstruction for TokenAddBankInstruction<'keypair> {
.0;
let bank = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"Bank".as_ref(),
self.group.as_ref(),
&self.token_index.to_le_bytes(),
&self.bank_num.to_le_bytes(),
],
@ -738,8 +738,8 @@ impl<'keypair> ClientInstruction for TokenAddBankInstruction<'keypair> {
.0;
let vault = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"Vault".as_ref(),
self.group.as_ref(),
&self.token_index.to_le_bytes(),
&self.bank_num.to_le_bytes(),
],
@ -751,7 +751,7 @@ impl<'keypair> ClientInstruction for TokenAddBankInstruction<'keypair> {
let mint = existing_bank_data.mint;
let mint_info = Pubkey::find_program_address(
&[self.group.as_ref(), b"MintInfo".as_ref(), mint.as_ref()],
&[b"MintInfo".as_ref(), self.group.as_ref(), mint.as_ref()],
&program_id,
)
.0;
@ -869,8 +869,8 @@ impl<'keypair> ClientInstruction for StubOracleSetInstruction<'keypair> {
// TODO: remove copy pasta of pda derivation, use reference
let oracle = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"StubOracle".as_ref(),
self.group.as_ref(),
self.mint.as_ref(),
],
&program_id,
@ -915,8 +915,8 @@ impl<'keypair> ClientInstruction for StubOracleCreate<'keypair> {
let oracle = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"StubOracle".as_ref(),
self.group.as_ref(),
self.mint.as_ref(),
],
&program_id,
@ -961,8 +961,8 @@ impl<'keypair> ClientInstruction for StubOracleCloseInstruction<'keypair> {
let oracle = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"StubOracle".as_ref(),
self.group.as_ref(),
self.mint.as_ref(),
],
&program_id,
@ -1017,7 +1017,7 @@ impl<'keypair> ClientInstruction for GroupCreateInstruction<'keypair> {
.0;
let insurance_vault = Pubkey::find_program_address(
&[group.as_ref(), b"InsuranceVault".as_ref()],
&[b"InsuranceVault".as_ref(), group.as_ref()],
&program_id,
)
.0;
@ -1059,7 +1059,7 @@ impl<'keypair> ClientInstruction for GroupCloseInstruction<'keypair> {
let instruction = Self::Instruction {};
let insurance_vault = Pubkey::find_program_address(
&[self.group.as_ref(), b"InsuranceVault".as_ref()],
&[b"InsuranceVault".as_ref(), self.group.as_ref()],
&program_id,
)
.0;
@ -1111,8 +1111,8 @@ impl<'keypair> ClientInstruction for AccountCreateInstruction<'keypair> {
let account = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"MangoAccount".as_ref(),
self.group.as_ref(),
self.owner.pubkey().as_ref(),
&self.account_num.to_le_bytes(),
],
@ -1165,8 +1165,8 @@ impl<'keypair> ClientInstruction for AccountExpandInstruction<'keypair> {
let account = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"MangoAccount".as_ref(),
self.group.as_ref(),
self.owner.pubkey().as_ref(),
&self.account_num.to_le_bytes(),
],
@ -1214,8 +1214,8 @@ impl<'keypair> ClientInstruction for AccountEditInstruction<'keypair> {
let account = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"MangoAccount".as_ref(),
self.group.as_ref(),
self.owner.pubkey().as_ref(),
&self.account_num.to_le_bytes(),
],
@ -1301,8 +1301,8 @@ impl<'keypair> ClientInstruction for Serum3RegisterMarketInstruction<'keypair> {
let serum_market = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"Serum3Market".as_ref(),
self.group.as_ref(),
self.serum_market_external.as_ref(),
],
&program_id,
@ -1349,8 +1349,8 @@ impl<'keypair> ClientInstruction for Serum3DeregisterMarketInstruction<'keypair>
let serum_market = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"Serum3Market".as_ref(),
self.group.as_ref(),
self.serum_market_external.as_ref(),
],
&program_id,
@ -1395,8 +1395,8 @@ impl<'keypair> ClientInstruction for Serum3CreateOpenOrdersInstruction<'keypair>
let serum_market: Serum3Market = account_loader.load(&self.serum_market).await.unwrap();
let open_orders = Pubkey::find_program_address(
&[
self.account.as_ref(),
b"Serum3OO".as_ref(),
self.account.as_ref(),
self.serum_market.as_ref(),
],
&program_id,
@ -1446,8 +1446,8 @@ impl<'keypair> ClientInstruction for Serum3CloseOpenOrdersInstruction<'keypair>
let serum_market: Serum3Market = account_loader.load(&self.serum_market).await.unwrap();
let open_orders = Pubkey::find_program_address(
&[
self.account.as_ref(),
b"Serum3OO".as_ref(),
self.account.as_ref(),
self.serum_market.as_ref(),
],
&program_id,
@ -1996,8 +1996,8 @@ impl<'keypair> ClientInstruction for LiqTokenBankruptcyInstruction<'keypair> {
let quote_mint_info = Pubkey::find_program_address(
&[
liqee.fixed.group.as_ref(),
b"MintInfo".as_ref(),
liqee.fixed.group.as_ref(),
group.insurance_mint.as_ref(),
],
&program_id,
@ -2006,7 +2006,7 @@ impl<'keypair> ClientInstruction for LiqTokenBankruptcyInstruction<'keypair> {
let quote_mint_info: MintInfo = account_loader.load(&quote_mint_info).await.unwrap();
let insurance_vault = Pubkey::find_program_address(
&[group_key.as_ref(), b"InsuranceVault".as_ref()],
&[b"InsuranceVault".as_ref(), group_key.as_ref()],
&program_id,
)
.0;
@ -2097,8 +2097,8 @@ impl<'keypair> ClientInstruction for PerpCreateMarketInstruction<'keypair> {
let perp_market = Pubkey::find_program_address(
&[
self.group.as_ref(),
b"PerpMarket".as_ref(),
self.group.as_ref(),
self.perp_market_index.to_le_bytes().as_ref(),
],
&program_id,

View File

@ -26,6 +26,7 @@ export class Bank {
public util0: I80F48;
public util1: I80F48;
public price: I80F48;
public collectedFeesNative: I80F48;
public loanFeeRate: I80F48;
public loanOriginationFeeRate: I80F48;
public initAssetWeight: I80F48;
@ -166,6 +167,7 @@ export class Bank {
this.rate0 = I80F48.from(rate0);
this.util1 = I80F48.from(util1);
this.rate1 = I80F48.from(rate1);
this.collectedFeesNative = I80F48.from(collectedFeesNative);
this.loanFeeRate = I80F48.from(loanFeeRate);
this.loanOriginationFeeRate = I80F48.from(loanOriginationFeeRate);
this.maintAssetWeight = I80F48.from(maintAssetWeight);

View File

@ -104,11 +104,18 @@ export class MangoClient {
public async groupEdit(
group: Group,
newAdmin: PublicKey,
newFastListingAdmin: PublicKey,
admin: PublicKey | undefined,
fastListingAdmin: PublicKey | undefined,
testing: number | undefined,
version: number | undefined,
): Promise<TransactionSignature> {
return await this.program.methods
.groupEdit(newAdmin, newFastListingAdmin)
.groupEdit(
admin ?? null,
fastListingAdmin ?? null,
testing ?? null,
version ?? null,
)
.accounts({
group: group.publicKey,
admin: (this.program.provider as AnchorProvider).wallet.publicKey,
@ -605,6 +612,21 @@ export class MangoClient {
});
}
public async getAllMangoAccounts(group: Group): Promise<MangoAccount[]> {
return (
await this.program.account.mangoAccount.all([
{
memcmp: {
bytes: group.publicKey.toBase58(),
offset: 8,
},
},
])
).map((pa) => {
return MangoAccount.from(pa.publicKey, pa.account);
});
}
public async closeMangoAccount(
group: Group,
mangoAccount: MangoAccount,

View File

@ -0,0 +1,116 @@
import { AnchorProvider, Wallet } from '@project-serum/anchor';
import { coder } from '@project-serum/anchor/dist/cjs/spl/token';
import { Connection, Keypair } from '@solana/web3.js';
import fs from 'fs';
import { ZERO_I80F48 } from '../accounts/I80F48';
import { MangoClient } from '../client';
import { MANGO_V4_ID } from '../constants';
async function main() {
const options = AnchorProvider.defaultOptions();
const connection = new Connection(process.env.MB_CLUSTER_URL!, options);
const admin = Keypair.fromSecretKey(
Buffer.from(
JSON.parse(fs.readFileSync(process.env.MB_PAYER_KEYPAIR!, 'utf-8')),
),
);
const adminWallet = new Wallet(admin);
const adminProvider = new AnchorProvider(connection, adminWallet, options);
const client = MangoClient.connect(
adminProvider,
'mainnet-beta',
MANGO_V4_ID['mainnet-beta'],
);
const group = await client.getGroupForCreator(admin.publicKey, 0);
console.log(`Group ${group.publicKey.toBase58()}`);
const banks = await client.getBanksForGroup(group);
const banksMapUsingTokenIndex = new Map(
banks.map((bank) => {
(bank as any).indexedDepositsByMangoAccounts = ZERO_I80F48;
(bank as any).indexedBorrowsByMangoAccounts = ZERO_I80F48;
return [bank.tokenIndex, bank];
}),
);
const mangoAccounts = await client.getAllMangoAccounts(group);
mangoAccounts.forEach((mangoAccount) =>
console.log(
`MangoAccount pk - ${mangoAccount.publicKey}, owner - ${mangoAccount.owner}`,
),
);
mangoAccounts.map((mangoAccount) =>
mangoAccount.tokensActive().forEach((token) => {
const bank = banksMapUsingTokenIndex.get(token.tokenIndex);
if (token.indexedPosition.isPos()) {
(bank as any).indexedDepositsByMangoAccounts = (
bank as any
).indexedDepositsByMangoAccounts.add(
token.indexedPosition.mul(
banksMapUsingTokenIndex.get(token.tokenIndex).depositIndex,
),
);
}
if (token.indexedPosition.isNeg()) {
(bank as any).indexedBorrowsByMangoAccounts = (
bank as any
).indexedBorrowsByMangoAccounts.add(
token.indexedPosition
.abs()
.mul(banksMapUsingTokenIndex.get(token.tokenIndex).borrowIndex),
);
}
}),
);
for (const bank of await Array.from(banksMapUsingTokenIndex.values()).sort(
(a, b) => a.tokenIndex - b.tokenIndex,
)) {
let res = `${bank.name}`;
res =
res +
`\n ${'collectedFeesNative'.padEnd(40)} ${bank.collectedFeesNative}` +
`\n ${'deposits'.padEnd(40)} ${bank.indexedDeposits.mul(
bank.depositIndex,
)}` +
`\n ${'deposits (sum over all mango accounts)'.padEnd(40)} ${
(bank as any).indexedDepositsByMangoAccounts
}` +
`\n ${'cachedIndexedTotalDeposits'.padEnd(40)} ${(
bank as any
).cachedIndexedTotalDeposits.mul(bank.depositIndex)}` +
`\n ${'indexedBorrows'.padEnd(40)} ${bank.indexedBorrows.mul(
bank.borrowIndex,
)}` +
`\n ${'borrows (sum over all mango accounts)'.padEnd(40)} ${
(bank as any).indexedBorrowsByMangoAccounts
}` +
`\n ${'cachedIndexedTotalBorrows'.padEnd(40)} ${(
bank as any
).cachedIndexedTotalBorrows.mul(bank.borrowIndex)}` +
`\n ${'avgUtilization'.padEnd(40)} ${bank.avgUtilization}` +
`\n ${'depositRate'.padEnd(40)} ${bank.getDepositRate()}` +
`\n ${'borrowRate'.padEnd(40)} ${bank.getBorrowRate()}` +
`\n ${'vault'.padEnd(40)} ${coder()
.accounts.decode(
'token',
await (
await client.program.provider.connection.getAccountInfo(bank.vault)
).data,
)
.amount.toNumber()}`;
console.log(`${res}`);
}
process.exit();
}
try {
main();
} catch (error) {
console.log(error);
}

View File

@ -45,15 +45,15 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "InsuranceVault"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
}
]
}
@ -110,12 +110,28 @@ export type MangoV4 = {
],
"args": [
{
"name": "newAdmin",
"type": "publicKey"
"name": "adminOpt",
"type": {
"option": "publicKey"
}
},
{
"name": "newFastListingAdmin",
"type": "publicKey"
"name": "fastListingAdminOpt",
"type": {
"option": "publicKey"
}
},
{
"name": "testingOpt",
"type": {
"option": "u8"
}
},
{
"name": "versionOpt",
"type": {
"option": "u8"
}
}
]
},
@ -174,16 +190,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Bank"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -203,16 +219,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Vault"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -232,16 +248,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "MintInfo"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "account",
"type": "publicKey",
@ -352,16 +368,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Bank"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -381,16 +397,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Vault"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -410,16 +426,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "MintInfo"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "account",
"type": "publicKey",
@ -591,16 +607,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Bank"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -620,16 +636,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Vault"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -646,27 +662,7 @@ export type MangoV4 = {
{
"name": "mintInfo",
"isMut": true,
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "MintInfo"
},
{
"kind": "account",
"type": "publicKey",
"account": "Mint",
"path": "mint"
}
]
}
"isSigner": false
},
{
"name": "payer",
@ -781,16 +777,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "MangoAccount"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "account",
"type": "publicKey",
@ -974,16 +970,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "StubOracle"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "account",
"type": "publicKey",
@ -1269,16 +1265,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Serum3Market"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "account",
"type": "publicKey",
@ -1389,16 +1385,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "account"
},
{
"kind": "const",
"type": "string",
"value": "Serum3OO"
},
{
"kind": "account",
"type": "publicKey",
"path": "account"
},
{
"kind": "account",
"type": "publicKey",
@ -2049,16 +2045,16 @@ export type MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "PerpMarket"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -4011,18 +4007,14 @@ export type MangoV4 = {
}
},
{
"name": "baseEntryLots",
"name": "quoteEntryNative",
"docs": [
"Tracks what the position is to calculate average entry & break even price"
"Tracks what the position is to calculate average entry & break even price"
],
"type": "i64"
},
{
"name": "quoteEntryNative",
"type": "i64"
},
{
"name": "quoteExitNative",
"name": "quoteRunningNative",
"type": "i64"
},
{
@ -5089,15 +5081,15 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "InsuranceVault"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
}
]
}
@ -5154,12 +5146,28 @@ export const IDL: MangoV4 = {
],
"args": [
{
"name": "newAdmin",
"type": "publicKey"
"name": "adminOpt",
"type": {
"option": "publicKey"
}
},
{
"name": "newFastListingAdmin",
"type": "publicKey"
"name": "fastListingAdminOpt",
"type": {
"option": "publicKey"
}
},
{
"name": "testingOpt",
"type": {
"option": "u8"
}
},
{
"name": "versionOpt",
"type": {
"option": "u8"
}
}
]
},
@ -5218,16 +5226,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Bank"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -5247,16 +5255,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Vault"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -5276,16 +5284,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "MintInfo"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "account",
"type": "publicKey",
@ -5396,16 +5404,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Bank"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -5425,16 +5433,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Vault"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -5454,16 +5462,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "MintInfo"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "account",
"type": "publicKey",
@ -5635,16 +5643,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Bank"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -5664,16 +5672,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Vault"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -5690,27 +5698,7 @@ export const IDL: MangoV4 = {
{
"name": "mintInfo",
"isMut": true,
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "MintInfo"
},
{
"kind": "account",
"type": "publicKey",
"account": "Mint",
"path": "mint"
}
]
}
"isSigner": false
},
{
"name": "payer",
@ -5825,16 +5813,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "MangoAccount"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "account",
"type": "publicKey",
@ -6018,16 +6006,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "StubOracle"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "account",
"type": "publicKey",
@ -6313,16 +6301,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "Serum3Market"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "account",
"type": "publicKey",
@ -6433,16 +6421,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "account"
},
{
"kind": "const",
"type": "string",
"value": "Serum3OO"
},
{
"kind": "account",
"type": "publicKey",
"path": "account"
},
{
"kind": "account",
"type": "publicKey",
@ -7093,16 +7081,16 @@ export const IDL: MangoV4 = {
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "const",
"type": "string",
"value": "PerpMarket"
},
{
"kind": "account",
"type": "publicKey",
"path": "group"
},
{
"kind": "arg",
"type": "u16",
@ -9055,18 +9043,14 @@ export const IDL: MangoV4 = {
}
},
{
"name": "baseEntryLots",
"name": "quoteEntryNative",
"docs": [
"Tracks what the position is to calculate average entry & break even price"
"Tracks what the position is to calculate average entry & break even price"
],
"type": "i64"
},
{
"name": "quoteEntryNative",
"type": "i64"
},
{
"name": "quoteExitNative",
"name": "quoteRunningNative",
"type": "i64"
},
{

View File

@ -204,6 +204,8 @@ async function main() {
group,
group.admin,
new PublicKey('Efhak3qj3MiyzgJr3cUUqXXz5wr3oYHt9sPzuqJf9eBN'),
undefined,
undefined,
);
console.log(`sig https://explorer.solana.com/tx/${sig}?cluster=devnet`);
console.log(`Registering MNGO...`);

View File

@ -10,6 +10,7 @@ import {
} from '../accounts/serum3';
import { MangoClient } from '../client';
import { MANGO_V4_ID } from '../constants';
import { toUiDecimalsForQuote } from '../utils';
//
// An example for users based on high level api i.e. the client
@ -240,11 +241,15 @@ async function main() {
);
console.log(
'...mangoAccount.getAssetsVal() ' +
toUiDecimalsForQuote(mangoAccount.getAssetsVal().toNumber()),
toUiDecimalsForQuote(
mangoAccount.getAssetsVal(HealthType.init).toNumber(),
),
);
console.log(
'...mangoAccount.getLiabsVal() ' +
toUiDecimalsForQuote(mangoAccount.getLiabsVal().toNumber()),
toUiDecimalsForQuote(
mangoAccount.getLiabsVal(HealthType.init).toNumber(),
),
);
console.log(
'...mangoAccount.getMaxWithdrawWithBorrowForToken(group, "SOL") ' +

View File

@ -209,7 +209,7 @@ async function main() {
);
console.log(`Registering MNGO...`);
await client.groupEdit(group, group.admin, group.admin);
await client.groupEdit(group, group.admin, group.admin, undefined, undefined);
const mngoMainnetMint = new PublicKey(MAINNET_MINTS.get('MNGO')!);
const mngoMainnetOracle = new PublicKey(MAINNET_ORACLES.get('MNGO')!);
await client.tokenRegisterTrustless(

View File

@ -3,6 +3,7 @@ import { Connection, Keypair } from '@solana/web3.js';
import fs from 'fs';
import { Serum3Side } from '../accounts/serum3';
import { MangoClient } from '../client';
import { MANGO_V4_ID } from '../constants';
//
// (untested?) script which closes a mango account cleanly, first closes all positions, withdraws all tokens and then closes it
@ -14,14 +15,15 @@ async function main() {
// user
const user = Keypair.fromSecretKey(
Buffer.from(
JSON.parse(fs.readFileSync(process.env.MB_PAYER_KEYPAIR!, 'utf-8')),
JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')),
),
);
const userWallet = new Wallet(user);
const userProvider = new AnchorProvider(connection, userWallet, options);
const client = await MangoClient.connectForGroupName(
const client = await MangoClient.connect(
userProvider,
'mainnet-beta.microwavedcola' /* Use ids json instead of getProgramAccounts */,
'mainnet-beta',
MANGO_V4_ID['mainnet-beta'],
);
console.log(`User ${userWallet.publicKey.toBase58()}`);
@ -34,7 +36,7 @@ async function main() {
console.log(`Admin ${admin.publicKey.toBase58()}`);
// fetch group
const group = await client.getGroupForCreator(admin.publicKey);
const group = await client.getGroupForCreator(admin.publicKey, 0);
console.log(`Found group ${group.publicKey.toBase58()}`);
// account