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:
Christian Kamm 2023-08-19 08:20:40 +02:00 committed by GitHub
parent 6fd158ff6c
commit 34a875d968
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 118 additions and 105 deletions

View File

@ -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();

View File

@ -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

View File

@ -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
//

View File

@ -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(())
}

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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(),
};

View File

@ -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

View File

@ -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'),

View File

@ -740,7 +740,7 @@ export class MangoClient {
accountNumber ?? 0,
tokenCount ?? 8,
serum3Count ?? 8,
perpCount ?? 8,
perpCount ?? 4,
perpOoCount ?? 32,
name ?? '',
)