Restrict account growth (#686)
Because of the 64-accounts-per-tx limit. Making a single account use more than half of these could be problematic.
This commit is contained in:
parent
6fd158ff6c
commit
34a875d968
|
@ -126,6 +126,9 @@ pub struct MangoAccount {
|
|||
pub perps: Vec<PerpPosition>,
|
||||
pub padding7: u32,
|
||||
pub perp_open_orders: Vec<PerpOpenOrder>,
|
||||
// WARNING: This does not have further fields, like tcs, intentionally:
|
||||
// There are existing accounts that don't have them and adding them here
|
||||
// would break backwards compatibility.
|
||||
}
|
||||
|
||||
impl MangoAccount {
|
||||
|
@ -170,9 +173,9 @@ impl MangoAccount {
|
|||
perp_oo_count: u8,
|
||||
token_conditional_swap_count: u8,
|
||||
) -> Result<usize> {
|
||||
require_gte!(16, token_count);
|
||||
require_gte!(8, token_count);
|
||||
require_gte!(8, serum3_count);
|
||||
require_gte!(8, perp_count);
|
||||
require_gte!(4, perp_count);
|
||||
require_gte!(64, perp_oo_count);
|
||||
require_gte!(64, token_conditional_swap_count);
|
||||
|
||||
|
@ -1540,7 +1543,7 @@ mod tests {
|
|||
account.tokens.resize(8, TokenPosition::default());
|
||||
account.tokens[0].token_index = 8;
|
||||
account.serum3.resize(8, Serum3Orders::default());
|
||||
account.perps.resize(8, PerpPosition::default());
|
||||
account.perps.resize(4, PerpPosition::default());
|
||||
account.perps[0].market_index = 9;
|
||||
account.perp_open_orders.resize(8, PerpOpenOrder::default());
|
||||
account.next_token_conditional_swap_id = 13;
|
||||
|
@ -1554,7 +1557,7 @@ mod tests {
|
|||
};
|
||||
assert_eq!(
|
||||
8 + account_bytes_with_tcs.len(),
|
||||
MangoAccount::space(8, 8, 8, 8, 0).unwrap()
|
||||
MangoAccount::space(8, 8, 4, 8, 0).unwrap()
|
||||
);
|
||||
|
||||
let account2 = MangoAccountValue::from_bytes(&account_bytes_without_tcs).unwrap();
|
||||
|
|
|
@ -68,13 +68,10 @@ async fn test_bankrupt_tokens_socialize_loss() -> Result<(), TransportError> {
|
|||
solana,
|
||||
AccountCreateInstruction {
|
||||
account_num: 0,
|
||||
token_count: 16,
|
||||
serum3_count: 8,
|
||||
perp_count: 8,
|
||||
perp_oo_count: 8,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
@ -325,13 +322,10 @@ async fn test_bankrupt_tokens_insurance_fund() -> Result<(), TransportError> {
|
|||
solana,
|
||||
AccountCreateInstruction {
|
||||
account_num: 2,
|
||||
token_count: 16,
|
||||
serum3_count: 8,
|
||||
perp_count: 8,
|
||||
perp_oo_count: 8,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
@ -379,13 +373,10 @@ async fn test_bankrupt_tokens_insurance_fund() -> Result<(), TransportError> {
|
|||
solana,
|
||||
AccountCreateInstruction {
|
||||
account_num: 0,
|
||||
token_count: 16,
|
||||
serum3_count: 8,
|
||||
perp_count: 8,
|
||||
perp_oo_count: 8,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -33,10 +33,11 @@ async fn test_basic() -> Result<(), TransportError> {
|
|||
solana,
|
||||
AccountCreateInstruction {
|
||||
account_num: 0,
|
||||
token_count: 8,
|
||||
token_count: 6,
|
||||
serum3_count: 7,
|
||||
perp_count: 0,
|
||||
perp_oo_count: 0,
|
||||
token_conditional_swap_count: 0,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
|
@ -46,46 +47,53 @@ async fn test_basic() -> Result<(), TransportError> {
|
|||
.unwrap()
|
||||
.account;
|
||||
let account_data: MangoAccount = solana.get_account(account).await;
|
||||
assert_eq!(account_data.tokens.len(), 6);
|
||||
assert_eq!(
|
||||
account_data.tokens.iter().filter(|t| t.is_active()).count(),
|
||||
0
|
||||
);
|
||||
assert_eq!(account_data.serum3.len(), 7);
|
||||
assert_eq!(
|
||||
account_data.serum3.iter().filter(|s| s.is_active()).count(),
|
||||
0
|
||||
);
|
||||
|
||||
assert_eq!(account_data.perps.len(), 0);
|
||||
assert_eq!(account_data.perp_open_orders.len(), 0);
|
||||
|
||||
send_tx(
|
||||
solana,
|
||||
AccountExpandInstruction {
|
||||
account_num: 0,
|
||||
token_count: 8,
|
||||
serum3_count: 8,
|
||||
perp_count: 4,
|
||||
perp_oo_count: 8,
|
||||
token_conditional_swap_count: 4,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.account;
|
||||
let account_data: MangoAccount = solana.get_account(account).await;
|
||||
assert_eq!(account_data.tokens.len(), 8);
|
||||
assert_eq!(
|
||||
account_data.tokens.iter().filter(|t| t.is_active()).count(),
|
||||
0
|
||||
);
|
||||
assert_eq!(account_data.serum3.len(), 7);
|
||||
assert_eq!(
|
||||
account_data.serum3.iter().filter(|s| s.is_active()).count(),
|
||||
0
|
||||
);
|
||||
|
||||
send_tx(
|
||||
solana,
|
||||
AccountExpandInstruction {
|
||||
account_num: 0,
|
||||
token_count: 16,
|
||||
serum3_count: 8,
|
||||
perp_count: 8,
|
||||
perp_oo_count: 8,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.account;
|
||||
let account_data: MangoAccount = solana.get_account(account).await;
|
||||
assert_eq!(account_data.tokens.len(), 16);
|
||||
assert_eq!(
|
||||
account_data.tokens.iter().filter(|t| t.is_active()).count(),
|
||||
0
|
||||
);
|
||||
assert_eq!(account_data.serum3.len(), 8);
|
||||
assert_eq!(
|
||||
account_data.serum3.iter().filter(|s| s.is_active()).count(),
|
||||
0
|
||||
);
|
||||
|
||||
assert_eq!(account_data.perps.len(), 4);
|
||||
assert_eq!(account_data.perp_open_orders.len(), 8);
|
||||
|
||||
//
|
||||
// TEST: Deposit funds
|
||||
//
|
||||
|
|
|
@ -9,7 +9,7 @@ async fn test_health_compute_tokens() -> Result<(), TransportError> {
|
|||
let admin = TestKeypair::new();
|
||||
let owner = context.users[0].key;
|
||||
let payer = context.users[1].key;
|
||||
let mints = &context.mints[0..10];
|
||||
let mints = &context.mints[0..8];
|
||||
|
||||
//
|
||||
// SETUP: Create a group and an account
|
||||
|
@ -28,7 +28,7 @@ async fn test_health_compute_tokens() -> Result<(), TransportError> {
|
|||
create_funded_account(&solana, group, owner, 0, &context.users[1], mints, 1000, 0).await;
|
||||
|
||||
// TODO: actual explicit CU comparisons.
|
||||
// On 2023-2-5 the final deposit costs 57622 CU and each new token increases it by roughly 2400 CU
|
||||
// On 2023-8-18 the final deposit costs 56245 CU and each new token increases it by roughly 2800 CU
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -64,13 +64,10 @@ async fn test_health_compute_serum() -> Result<(), TransportError> {
|
|||
solana,
|
||||
AccountCreateInstruction {
|
||||
account_num: 0,
|
||||
token_count: 16,
|
||||
serum3_count: 8,
|
||||
perp_count: 8,
|
||||
perp_oo_count: 8,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
@ -164,7 +161,7 @@ async fn test_health_compute_perp() -> Result<(), TransportError> {
|
|||
let admin = TestKeypair::new();
|
||||
let owner = context.users[0].key;
|
||||
let payer = context.users[1].key;
|
||||
let mints = &context.mints[0..8];
|
||||
let mints = &context.mints[0..5];
|
||||
let payer_mint_accounts = &context.users[1].token_accounts[0..mints.len()];
|
||||
|
||||
//
|
||||
|
@ -266,7 +263,7 @@ async fn test_health_compute_perp() -> Result<(), TransportError> {
|
|||
}
|
||||
|
||||
// TODO: actual explicit CU comparisons.
|
||||
// On 2023-2-5 the final deposit costs 60732 CU and each new market increases it by roughly 3400 CU
|
||||
// On 2023-8-18 the final deposit costs 51879 CU and each new market increases it by roughly 4100 CU
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -68,19 +68,6 @@ async fn test_liq_perps_bankruptcy() -> Result<(), TransportError> {
|
|||
let base_token = &tokens[1]; // used for perp market
|
||||
let collateral_token = &tokens[2]; // used for adjusting account health
|
||||
|
||||
// deposit some funds, to the vaults aren't empty
|
||||
let liqor = create_funded_account(
|
||||
&solana,
|
||||
group,
|
||||
owner,
|
||||
250,
|
||||
&context.users[1],
|
||||
mints,
|
||||
10000,
|
||||
0,
|
||||
)
|
||||
.await;
|
||||
|
||||
// all perp markets used here default to price = 1.0, base_lot_size = 100
|
||||
let price_lots = 100;
|
||||
|
||||
|
@ -95,6 +82,18 @@ async fn test_liq_perps_bankruptcy() -> Result<(), TransportError> {
|
|||
let adj_price = 1.0 + pnl as f64 / -100.0;
|
||||
let adj_price_lots = (price_lots as f64 * adj_price) as i64;
|
||||
|
||||
let fresh_liqor = create_funded_account(
|
||||
&solana,
|
||||
group,
|
||||
owner,
|
||||
200 + perp_market_index as u32,
|
||||
&context_ref.users[1],
|
||||
mints,
|
||||
10000,
|
||||
0,
|
||||
)
|
||||
.await;
|
||||
|
||||
let mango_v4::accounts::PerpCreateMarket { perp_market, .. } = send_tx(
|
||||
solana,
|
||||
PerpCreateMarketInstruction {
|
||||
|
@ -236,7 +235,7 @@ async fn test_liq_perps_bankruptcy() -> Result<(), TransportError> {
|
|||
health as f64
|
||||
);
|
||||
|
||||
(perp_market, account)
|
||||
(perp_market, account, fresh_liqor)
|
||||
};
|
||||
let mut setup_perp = |health: i64, pnl: i64, settle_limit: i64| {
|
||||
let out = setup_perp_inner(perp_market_index, health, pnl, settle_limit);
|
||||
|
@ -265,7 +264,7 @@ async fn test_liq_perps_bankruptcy() -> Result<(), TransportError> {
|
|||
(settlement, insur, loss)
|
||||
};
|
||||
|
||||
let liqor_info = |perp_market: Pubkey| async move {
|
||||
let liqor_info = |perp_market: Pubkey, liqor: Pubkey| async move {
|
||||
let perp_market = solana.get_account::<PerpMarket>(perp_market).await;
|
||||
let liqor_data = solana.get_account::<MangoAccount>(liqor).await;
|
||||
let liqor_perp = liqor_data
|
||||
|
@ -278,7 +277,7 @@ async fn test_liq_perps_bankruptcy() -> Result<(), TransportError> {
|
|||
};
|
||||
|
||||
{
|
||||
let (perp_market, account) = setup_perp(-28, -50, -10).await;
|
||||
let (perp_market, account, liqor) = setup_perp(-28, -50, -10).await;
|
||||
let liqor_quote_before = account_position(solana, liqor, quote_token.bank).await;
|
||||
|
||||
send_tx(
|
||||
|
@ -306,12 +305,12 @@ async fn test_liq_perps_bankruptcy() -> Result<(), TransportError> {
|
|||
let acc_data = solana.get_account::<MangoAccount>(account).await;
|
||||
assert_eq!(acc_data.perps[0].quote_position_native(), -49);
|
||||
assert_eq!(acc_data.being_liquidated, 1);
|
||||
let (_liqor_data, liqor_perp) = liqor_info(perp_market).await;
|
||||
let (_liqor_data, liqor_perp) = liqor_info(perp_market, liqor).await;
|
||||
assert_eq!(liqor_perp.quote_position_native(), -1);
|
||||
}
|
||||
|
||||
{
|
||||
let (perp_market, account) = setup_perp(-28, -50, -10).await;
|
||||
let (perp_market, account, liqor) = setup_perp(-28, -50, -10).await;
|
||||
fund_insurance(2).await;
|
||||
let liqor_quote_before = account_position(solana, liqor, quote_token.bank).await;
|
||||
|
||||
|
@ -344,12 +343,12 @@ async fn test_liq_perps_bankruptcy() -> Result<(), TransportError> {
|
|||
0.1
|
||||
));
|
||||
assert_eq!(acc_data.being_liquidated, 0);
|
||||
let (_liqor_data, liqor_perp) = liqor_info(perp_market).await;
|
||||
let (_liqor_data, liqor_perp) = liqor_info(perp_market, liqor).await;
|
||||
assert_eq!(liqor_perp.quote_position_native(), -11);
|
||||
}
|
||||
|
||||
{
|
||||
let (perp_market, account) = setup_perp(-28, -50, -10).await;
|
||||
let (perp_market, account, liqor) = setup_perp(-28, -50, -10).await;
|
||||
fund_insurance(5).await;
|
||||
|
||||
send_tx(
|
||||
|
@ -372,7 +371,7 @@ async fn test_liq_perps_bankruptcy() -> Result<(), TransportError> {
|
|||
|
||||
// no insurance
|
||||
{
|
||||
let (perp_market, account) = setup_perp(-28, -50, -10).await;
|
||||
let (perp_market, account, liqor) = setup_perp(-28, -50, -10).await;
|
||||
|
||||
send_tx(
|
||||
solana,
|
||||
|
@ -391,7 +390,7 @@ async fn test_liq_perps_bankruptcy() -> Result<(), TransportError> {
|
|||
|
||||
// no settlement: no settle health
|
||||
{
|
||||
let (perp_market, account) = setup_perp(-200, -50, -10).await;
|
||||
let (perp_market, account, liqor) = setup_perp(-200, -50, -10).await;
|
||||
fund_insurance(5).await;
|
||||
|
||||
send_tx(
|
||||
|
@ -411,7 +410,7 @@ async fn test_liq_perps_bankruptcy() -> Result<(), TransportError> {
|
|||
|
||||
// no settlement: no settle limit
|
||||
{
|
||||
let (perp_market, account) = setup_perp(-40, -50, 0).await;
|
||||
let (perp_market, account, liqor) = setup_perp(-40, -50, 0).await;
|
||||
// no insurance
|
||||
|
||||
send_tx(
|
||||
|
@ -431,7 +430,7 @@ async fn test_liq_perps_bankruptcy() -> Result<(), TransportError> {
|
|||
|
||||
// no socialized loss: fully covered by insurance fund
|
||||
{
|
||||
let (perp_market, account) = setup_perp(-40, -50, -5).await;
|
||||
let (perp_market, account, liqor) = setup_perp(-40, -50, -5).await;
|
||||
fund_insurance(42).await;
|
||||
|
||||
send_tx(
|
||||
|
|
|
@ -197,13 +197,10 @@ async fn test_liq_tokens_with_token() -> Result<(), TransportError> {
|
|||
solana,
|
||||
AccountCreateInstruction {
|
||||
account_num: 2,
|
||||
token_count: 16,
|
||||
serum3_count: 8,
|
||||
perp_count: 8,
|
||||
perp_oo_count: 8,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
@ -233,13 +230,10 @@ async fn test_liq_tokens_with_token() -> Result<(), TransportError> {
|
|||
solana,
|
||||
AccountCreateInstruction {
|
||||
account_num: 0,
|
||||
token_count: 16,
|
||||
serum3_count: 8,
|
||||
perp_count: 8,
|
||||
perp_oo_count: 8,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -58,13 +58,10 @@ async fn test_margin_trade() -> Result<(), BanksClientError> {
|
|||
solana,
|
||||
AccountCreateInstruction {
|
||||
account_num: 0,
|
||||
token_count: 16,
|
||||
serum3_count: 8,
|
||||
perp_count: 8,
|
||||
perp_oo_count: 8,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -30,13 +30,10 @@ async fn test_position_lifetime() -> Result<(), TransportError> {
|
|||
solana,
|
||||
AccountCreateInstruction {
|
||||
account_num: 0,
|
||||
token_count: 16,
|
||||
serum3_count: 8,
|
||||
perp_count: 8,
|
||||
perp_oo_count: 8,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -50,6 +50,20 @@ async fn test_token_conditional_swap() -> Result<(), TransportError> {
|
|||
0,
|
||||
)
|
||||
.await;
|
||||
let no_tcs_account = send_tx(
|
||||
solana,
|
||||
AccountCreateInstruction {
|
||||
account_num: 2,
|
||||
token_conditional_swap_count: 0,
|
||||
group,
|
||||
owner,
|
||||
payer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.account;
|
||||
|
||||
send_tx(
|
||||
solana,
|
||||
|
@ -73,7 +87,7 @@ async fn test_token_conditional_swap() -> Result<(), TransportError> {
|
|||
let tx_result = send_tx(
|
||||
solana,
|
||||
TokenConditionalSwapCreateInstruction {
|
||||
account,
|
||||
account: no_tcs_account,
|
||||
owner,
|
||||
buy_mint: quote_token.mint.pubkey,
|
||||
sell_mint: base_token.mint.pubkey,
|
||||
|
@ -96,10 +110,10 @@ async fn test_token_conditional_swap() -> Result<(), TransportError> {
|
|||
solana,
|
||||
AccountExpandInstruction {
|
||||
account_num: 0,
|
||||
token_count: 16,
|
||||
token_count: 8,
|
||||
serum3_count: 8,
|
||||
perp_count: 8,
|
||||
perp_oo_count: 8,
|
||||
perp_count: 4,
|
||||
perp_oo_count: 16,
|
||||
token_conditional_swap_count: 2,
|
||||
group,
|
||||
owner,
|
||||
|
|
|
@ -1814,10 +1814,26 @@ pub struct AccountCreateInstruction {
|
|||
pub serum3_count: u8,
|
||||
pub perp_count: u8,
|
||||
pub perp_oo_count: u8,
|
||||
pub token_conditional_swap_count: u8,
|
||||
pub group: Pubkey,
|
||||
pub owner: TestKeypair,
|
||||
pub payer: TestKeypair,
|
||||
}
|
||||
impl Default for AccountCreateInstruction {
|
||||
fn default() -> Self {
|
||||
AccountCreateInstruction {
|
||||
account_num: 0,
|
||||
token_count: 8,
|
||||
serum3_count: 8,
|
||||
perp_count: 4,
|
||||
perp_oo_count: 16,
|
||||
token_conditional_swap_count: 1,
|
||||
group: Default::default(),
|
||||
owner: Default::default(),
|
||||
payer: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ClientInstruction for AccountCreateInstruction {
|
||||
type Accounts = mango_v4::accounts::AccountCreate;
|
||||
|
@ -1833,7 +1849,7 @@ impl ClientInstruction for AccountCreateInstruction {
|
|||
serum3_count: self.serum3_count,
|
||||
perp_count: self.perp_count,
|
||||
perp_oo_count: self.perp_oo_count,
|
||||
token_conditional_swap_count: 0,
|
||||
token_conditional_swap_count: self.token_conditional_swap_count,
|
||||
name: "my_mango_account".to_string(),
|
||||
};
|
||||
|
||||
|
|
|
@ -163,13 +163,10 @@ pub async fn create_funded_account(
|
|||
solana,
|
||||
AccountCreateInstruction {
|
||||
account_num,
|
||||
token_count: 16,
|
||||
serum3_count: 8,
|
||||
perp_count: 8,
|
||||
perp_oo_count: 8,
|
||||
group,
|
||||
owner,
|
||||
payer: payer.key,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -370,7 +370,7 @@ async function createMangoAccount(): Promise<void> {
|
|||
const group = await client.getGroup(new PublicKey(GROUP_PK));
|
||||
|
||||
const ix = await client.program.methods
|
||||
.accountCreate(0, 8, 8, 8, 32, 'Mango DAO 0')
|
||||
.accountCreate(0, 8, 8, 4, 32, 'Mango DAO 0')
|
||||
.accounts({
|
||||
group: group.publicKey,
|
||||
owner: new PublicKey('5tgfd6XgwiXB9otEnzFpXK11m7Q7yZUaAJzWK4oT5UGF'),
|
||||
|
|
|
@ -740,7 +740,7 @@ export class MangoClient {
|
|||
accountNumber ?? 0,
|
||||
tokenCount ?? 8,
|
||||
serum3Count ?? 8,
|
||||
perpCount ?? 8,
|
||||
perpCount ?? 4,
|
||||
perpOoCount ?? 32,
|
||||
name ?? '',
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue