Drop AccountLoaderDynamic

This gives us better compatibility with released anchor versions.

Instead of using AccountLoaderDynamic<MangoAccount>, we now use
a standard AccountLoader<MangoAccountFixed>. This will generally work
(except for load_init(), which is dangerous).
A new trait, MangoAccountLoader, provides load_full(), load_full_mut()
etc on the AccountLoader<MangoAccountFixed> to create accessor structs
that can read and write to the dynamic part of the mango account data.
This commit is contained in:
Christian Kamm 2022-12-29 11:48:46 +01:00
parent 9ada3f0574
commit 31bd72e84a
34 changed files with 221 additions and 360 deletions

2
anchor

@ -1 +1 @@
Subproject commit 309c2c2f4cce7c0a13d307fab3c7e2985bff3fa5
Subproject commit b3707b1faaf6816cb3dd600074c81a39d373e952

View File

@ -1,7 +1,7 @@
use anchor_lang::Discriminator;
use arrayref::array_ref;
use mango_v4::state::{Bank, MangoAccount, MangoAccountRefWithHeader, MintInfo, PerpMarket};
use mango_v4::state::{Bank, MangoAccount, MangoAccountLoadedRef, MintInfo, PerpMarket};
use solana_sdk::account::{AccountSharedData, ReadableAccount};
use solana_sdk::pubkey::Pubkey;
@ -10,7 +10,7 @@ pub fn is_mango_account<'a>(
account: &'a AccountSharedData,
program_id: &Pubkey,
group_id: &Pubkey,
) -> Option<MangoAccountRefWithHeader<'a>> {
) -> Option<MangoAccountLoadedRef<'a>> {
let data = account.data();
if account.owner() != program_id || data.len() < 8 {
return None;
@ -21,7 +21,7 @@ pub fn is_mango_account<'a>(
return None;
}
if let Ok(mango_account) = MangoAccountRefWithHeader::from_bytes(&data[8..]) {
if let Ok(mango_account) = MangoAccountLoadedRef::from_bytes(&data[8..]) {
if mango_account.fixed.group != *group_id {
return None;
}

View File

@ -14,7 +14,7 @@ pub struct AccountClose<'info> {
has_one = owner,
close = sol_destination
)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(mut)]
@ -25,7 +25,7 @@ pub struct AccountClose<'info> {
}
pub fn account_close(ctx: Context<AccountClose>, force_close: bool) -> Result<()> {
let account = ctx.accounts.account.load_mut()?;
let account = ctx.accounts.account.load_full_mut()?;
if !ctx.accounts.group.load()?.is_testing() {
require!(!force_close, MangoError::SomeError);

View File

@ -16,7 +16,7 @@ pub struct AccountCreate<'info> {
payer = payer,
space = MangoAccount::space(token_count, serum3_count, perp_count, perp_oo_count)?,
)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(mut)]
@ -34,7 +34,7 @@ pub fn account_create(
perp_oo_count: u8,
name: String,
) -> Result<()> {
let mut account = ctx.accounts.account.load_init()?;
let mut account = ctx.accounts.account.load_full_init()?;
msg!(
"Initialized account with header version {}",

View File

@ -13,7 +13,7 @@ pub struct AccountEdit<'info> {
has_one = group,
has_one = owner
)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
}
@ -28,7 +28,7 @@ pub fn account_edit(
MangoError::SomeError
);
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
// note: unchanged fields are inline, and match exact definition in create_account
// please maintain, and don't remove, makes it easy to reason about which support modification by owner

View File

@ -12,7 +12,7 @@ pub struct AccountExpand<'info> {
has_one = group,
has_one = owner
)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(mut)]
@ -53,7 +53,7 @@ pub fn account_expand(
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()?;
let mut account = ctx.accounts.account.load_full_mut()?;
account.expand_dynamic_content(token_count, serum3_count, perp_count, perp_oo_count)?;
Ok(())

View File

@ -6,13 +6,13 @@ pub struct ComputeAccountData<'info> {
pub group: AccountLoader<'info, Group>,
#[account(has_one = group)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
}
pub fn compute_account_data(ctx: Context<ComputeAccountData>) -> Result<()> {
let group_pk = ctx.accounts.group.key();
let account = ctx.accounts.account.load()?;
let account = ctx.accounts.account.load_full()?;
let account_retriever = ScanningAccountRetriever::new(ctx.remaining_accounts, &group_pk)?;

View File

@ -3,8 +3,7 @@ use crate::error::*;
use crate::group_seeds;
use crate::health::{new_fixed_order_account_retriever, new_health_cache, AccountRetriever};
use crate::logs::{FlashLoanLog, FlashLoanTokenDetail, TokenBalanceLog};
use crate::state::MangoAccount;
use crate::state::{AccountLoaderDynamic, Bank, Group, TokenIndex};
use crate::state::*;
use crate::util::checked_math as cm;
use anchor_lang::prelude::*;
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
@ -32,7 +31,7 @@ pub mod jupiter_mainnet_3 {
/// 4. the mango group
#[derive(Accounts)]
pub struct FlashLoanBegin<'info> {
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
// owner is checked at #1
pub owner: Signer<'info>,
@ -55,7 +54,7 @@ pub struct FlashLoanBegin<'info> {
#[derive(Accounts)]
pub struct FlashLoanEnd<'info> {
#[account(mut)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
// owner is checked at #1
pub owner: Signer<'info>,
@ -75,7 +74,7 @@ pub fn flash_loan_begin<'key, 'accounts, 'remaining, 'info>(
ctx: Context<'key, 'accounts, 'remaining, 'info, FlashLoanBegin<'info>>,
loan_amounts: Vec<u64>,
) -> Result<()> {
let account = ctx.accounts.account.load_mut()?;
let account = ctx.accounts.account.load_full_mut()?;
// account constraint #1
require!(
@ -239,7 +238,7 @@ pub fn flash_loan_end<'key, 'accounts, 'remaining, 'info>(
ctx: Context<'key, 'accounts, 'remaining, 'info, FlashLoanEnd<'info>>,
flash_loan_type: FlashLoanType,
) -> Result<()> {
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
// account constraint #1
require!(

View File

@ -19,7 +19,7 @@ pub struct HealthRegionBegin<'info> {
pub instructions: UncheckedAccount<'info>,
#[account(mut)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
}
/// Ends a health region.
@ -28,7 +28,7 @@ pub struct HealthRegionBegin<'info> {
#[derive(Accounts)]
pub struct HealthRegionEnd<'info> {
#[account(mut)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
}
pub fn health_region_begin<'key, 'accounts, 'remaining, 'info>(
@ -69,7 +69,7 @@ pub fn health_region_begin<'key, 'accounts, 'remaining, 'info>(
);
}
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
require_msg!(
!account.fixed.is_in_health_region(),
"account must not already be health wrapped"
@ -91,7 +91,7 @@ pub fn health_region_begin<'key, 'accounts, 'remaining, 'info>(
pub fn health_region_end<'key, 'accounts, 'remaining, 'info>(
ctx: Context<'key, 'accounts, 'remaining, 'info, HealthRegionEnd<'info>>,
) -> Result<()> {
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
require_msg!(
account.fixed.is_in_health_region(),
"account must be health wrapped"

View File

@ -1,14 +1,14 @@
use anchor_lang::prelude::*;
use crate::error::MangoError;
use crate::state::{AccountLoaderDynamic, BookSide, Group, MangoAccount, Orderbook, PerpMarket};
use crate::state::{BookSide, Group, MangoAccountFixed, MangoAccountLoader, Orderbook, PerpMarket};
#[derive(Accounts)]
pub struct PerpCancelAllOrders<'info> {
pub group: AccountLoader<'info, Group>,
#[account(mut, has_one = group)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(
@ -25,7 +25,7 @@ pub struct PerpCancelAllOrders<'info> {
}
pub fn perp_cancel_all_orders(ctx: Context<PerpCancelAllOrders>, limit: u8) -> Result<()> {
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
MangoError::SomeError

View File

@ -2,7 +2,7 @@ use anchor_lang::prelude::*;
use crate::error::MangoError;
use crate::state::{
AccountLoaderDynamic, BookSide, Group, MangoAccount, Orderbook, PerpMarket, Side,
BookSide, Group, MangoAccountFixed, MangoAccountLoader, Orderbook, PerpMarket, Side,
};
#[derive(Accounts)]
@ -10,7 +10,7 @@ pub struct PerpCancelAllOrdersBySide<'info> {
pub group: AccountLoader<'info, Group>,
#[account(mut, has_one = group)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(
@ -31,7 +31,7 @@ pub fn perp_cancel_all_orders_by_side(
side_option: Option<Side>,
limit: u8,
) -> Result<()> {
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
require_keys_eq!(account.fixed.group, ctx.accounts.group.key());
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
@ -40,8 +40,8 @@ pub fn perp_cancel_all_orders_by_side(
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
let mut book = Orderbook {
bids: ctx.accounts.bids.load_init()?,
asks: ctx.accounts.asks.load_init()?,
bids: ctx.accounts.bids.load_mut()?,
asks: ctx.accounts.asks.load_mut()?,
};
book.cancel_all_orders(

View File

@ -1,14 +1,14 @@
use anchor_lang::prelude::*;
use crate::error::*;
use crate::state::{AccountLoaderDynamic, BookSide, Group, MangoAccount, Orderbook, PerpMarket};
use crate::state::{BookSide, Group, MangoAccountFixed, MangoAccountLoader, Orderbook, PerpMarket};
#[derive(Accounts)]
pub struct PerpCancelOrder<'info> {
pub group: AccountLoader<'info, Group>,
#[account(mut, has_one = group)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(
@ -25,7 +25,7 @@ pub struct PerpCancelOrder<'info> {
}
pub fn perp_cancel_order(ctx: Context<PerpCancelOrder>, order_id: u128) -> Result<()> {
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
MangoError::SomeError

View File

@ -1,14 +1,14 @@
use anchor_lang::prelude::*;
use crate::error::*;
use crate::state::{AccountLoaderDynamic, BookSide, Group, MangoAccount, Orderbook, PerpMarket};
use crate::state::{BookSide, Group, MangoAccountFixed, MangoAccountLoader, Orderbook, PerpMarket};
#[derive(Accounts)]
pub struct PerpCancelOrderByClientOrderId<'info> {
pub group: AccountLoader<'info, Group>,
#[account(mut, has_one = group)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(
@ -28,7 +28,7 @@ pub fn perp_cancel_order_by_client_order_id(
ctx: Context<PerpCancelOrderByClientOrderId>,
client_order_id: u64,
) -> Result<()> {
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
MangoError::SomeError

View File

@ -2,7 +2,7 @@ use anchor_lang::prelude::*;
use bytemuck::cast_ref;
use crate::error::MangoError;
use crate::state::{AccountLoaderDynamic, EventQueue, MangoAccount};
use crate::state::{EventQueue, MangoAccountFixed, MangoAccountLoader};
use crate::state::{EventType, FillEvent, Group, OutEvent, PerpMarket};
use crate::logs::{emit_perp_balances, FillLog};
@ -55,9 +55,9 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
continue;
}
let mal: AccountLoaderDynamic<MangoAccount> =
AccountLoaderDynamic::try_from(ai)?;
let mut ma = mal.load_mut()?;
let mal: AccountLoader<MangoAccountFixed> =
AccountLoader::try_from(ai)?;
let mut ma = mal.load_full_mut()?;
ma.execute_perp_maker(
perp_market.perp_market_index,
&mut perp_market,
@ -90,9 +90,9 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
continue;
}
let mal: AccountLoaderDynamic<MangoAccount> =
AccountLoaderDynamic::try_from(ai)?;
let mut maker = mal.load_mut()?;
let mal: AccountLoader<MangoAccountFixed> =
AccountLoader::try_from(ai)?;
let mut maker = mal.load_full_mut()?;
match mango_account_ais.iter().find(|ai| ai.key == &fill.taker) {
None => {
@ -106,9 +106,9 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
continue;
}
let mal: AccountLoaderDynamic<MangoAccount> =
AccountLoaderDynamic::try_from(ai)?;
let mut taker = mal.load_mut()?;
let mal: AccountLoader<MangoAccountFixed> =
AccountLoader::try_from(ai)?;
let mut taker = mal.load_full_mut()?;
maker.execute_perp_maker(
perp_market.perp_market_index,
@ -174,9 +174,8 @@ pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Res
continue;
}
let mal: AccountLoaderDynamic<MangoAccount> =
AccountLoaderDynamic::try_from(ai)?;
let mut ma = mal.load_mut()?;
let mal: AccountLoader<MangoAccountFixed> = AccountLoader::try_from(ai)?;
let mut ma = mal.load_full_mut()?;
ma.remove_perp_order(out.owner_slot as usize, out.quantity)?;
}

View File

@ -12,7 +12,7 @@ pub struct PerpDeactivatePosition<'info> {
has_one = group
// owner is checked at #1
)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(has_one = group)]
@ -20,7 +20,7 @@ pub struct PerpDeactivatePosition<'info> {
}
pub fn perp_deactivate_position(ctx: Context<PerpDeactivatePosition>) -> Result<()> {
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
// account constraint #1
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),

View File

@ -28,14 +28,14 @@ pub struct PerpLiqBankruptcy<'info> {
has_one = group
// liqor_owner is checked at #1
)]
pub liqor: AccountLoaderDynamic<'info, MangoAccount>,
pub liqor: AccountLoader<'info, MangoAccountFixed>,
pub liqor_owner: Signer<'info>,
#[account(
mut,
has_one = group
)]
pub liqee: AccountLoaderDynamic<'info, MangoAccount>,
pub liqee: AccountLoader<'info, MangoAccountFixed>,
#[account(
mut,
@ -78,7 +78,7 @@ pub fn perp_liq_bankruptcy(ctx: Context<PerpLiqBankruptcy>, max_liab_transfer: u
let group = ctx.accounts.group.load()?;
let group_pk = &ctx.accounts.group.key();
let mut liqor = ctx.accounts.liqor.load_mut()?;
let mut liqor = ctx.accounts.liqor.load_full_mut()?;
// account constraint #1
require!(
liqor
@ -88,7 +88,7 @@ pub fn perp_liq_bankruptcy(ctx: Context<PerpLiqBankruptcy>, max_liab_transfer: u
);
require!(!liqor.fixed.being_liquidated(), MangoError::BeingLiquidated);
let mut liqee = ctx.accounts.liqee.load_mut()?;
let mut liqee = ctx.accounts.liqee.load_full_mut()?;
let mut liqee_health_cache = {
let account_retriever = ScanningAccountRetriever::new(ctx.remaining_accounts, group_pk)?;
new_health_cache(&liqee.borrow(), &account_retriever)

View File

@ -24,11 +24,11 @@ pub struct PerpLiqBasePosition<'info> {
has_one = group
// liqor_owner is checked at #1
)]
pub liqor: AccountLoaderDynamic<'info, MangoAccount>,
pub liqor: AccountLoader<'info, MangoAccountFixed>,
pub liqor_owner: Signer<'info>,
#[account(mut, has_one = group)]
pub liqee: AccountLoaderDynamic<'info, MangoAccount>,
pub liqee: AccountLoader<'info, MangoAccountFixed>,
}
pub fn perp_liq_base_position(
@ -37,7 +37,7 @@ pub fn perp_liq_base_position(
) -> Result<()> {
let group_pk = &ctx.accounts.group.key();
let mut liqor = ctx.accounts.liqor.load_mut()?;
let mut liqor = ctx.accounts.liqor.load_full_mut()?;
// account constraint #1
require!(
liqor
@ -47,7 +47,7 @@ pub fn perp_liq_base_position(
);
require!(!liqor.fixed.being_liquidated(), MangoError::BeingLiquidated);
let mut liqee = ctx.accounts.liqee.load_mut()?;
let mut liqee = ctx.accounts.liqee.load_full_mut()?;
// Initial liqee health check
let mut liqee_health_cache = {

View File

@ -10,7 +10,7 @@ pub struct PerpLiqForceCancelOrders<'info> {
pub group: AccountLoader<'info, Group>,
#[account(mut, has_one = group)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
#[account(
mut,
@ -29,7 +29,7 @@ pub fn perp_liq_force_cancel_orders(
ctx: Context<PerpLiqForceCancelOrders>,
limit: u8,
) -> Result<()> {
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
//
// Check liqee health if liquidation is allowed

View File

@ -3,9 +3,9 @@ use anchor_lang::prelude::*;
use crate::accounts_zerocopy::*;
use crate::error::*;
use crate::health::{new_fixed_order_account_retriever, new_health_cache};
use crate::state::MangoAccount;
use crate::state::{
AccountLoaderDynamic, BookSide, EventQueue, Group, Order, Orderbook, PerpMarket,
BookSide, EventQueue, Group, MangoAccountFixed, MangoAccountLoader, Order, Orderbook,
PerpMarket,
};
#[derive(Accounts)]
@ -13,7 +13,7 @@ pub struct PerpPlaceOrder<'info> {
pub group: AccountLoader<'info, Group>,
#[account(mut, has_one = group)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(
@ -64,7 +64,7 @@ pub fn perp_place_order(ctx: Context<PerpPlaceOrder>, order: Order, limit: u8) -
perp_market.update_funding_and_stable_price(&book, oracle_price, now_ts)?;
}
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
MangoError::SomeError

View File

@ -6,8 +6,7 @@ use crate::accounts_zerocopy::*;
use crate::error::*;
use crate::health::{compute_health, new_fixed_order_account_retriever, HealthType};
use crate::state::Bank;
use crate::state::MangoAccount;
use crate::state::{AccountLoaderDynamic, Group, PerpMarket};
use crate::state::{Group, MangoAccountFixed, MangoAccountLoader, PerpMarket};
use crate::logs::{emit_perp_balances, PerpSettleFeesLog, TokenBalanceLog};
@ -20,7 +19,7 @@ pub struct PerpSettleFees<'info> {
// This account MUST have a loss
#[account(mut, has_one = group)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
/// CHECK: Oracle can have different account types, constrained by address in perp_market
pub oracle: UncheckedAccount<'info>,
@ -40,7 +39,7 @@ pub fn perp_settle_fees(ctx: Context<PerpSettleFees>, max_settle_amount: u64) ->
MangoError::MaxSettleAmountMustBeGreaterThanZero
);
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
let mut bank = ctx.accounts.settle_bank.load_mut()?;
let mut perp_market = ctx.accounts.perp_market.load_mut()?;

View File

@ -7,8 +7,7 @@ use crate::error::*;
use crate::health::{new_health_cache, HealthType, ScanningAccountRetriever};
use crate::logs::{emit_perp_balances, PerpSettlePnlLog, TokenBalanceLog};
use crate::state::Bank;
use crate::state::MangoAccount;
use crate::state::{AccountLoaderDynamic, Group, PerpMarket};
use crate::state::{Group, MangoAccountFixed, MangoAccountLoader, PerpMarket};
#[derive(Accounts)]
pub struct PerpSettlePnl<'info> {
@ -19,7 +18,7 @@ pub struct PerpSettlePnl<'info> {
has_one = group,
// settler_owner is checked at #1
)]
pub settler: AccountLoaderDynamic<'info, MangoAccount>,
pub settler: AccountLoader<'info, MangoAccountFixed>,
pub settler_owner: Signer<'info>,
#[account(has_one = group, has_one = oracle)]
@ -27,10 +26,10 @@ pub struct PerpSettlePnl<'info> {
// This account MUST be profitable
#[account(mut, has_one = group)]
pub account_a: AccountLoaderDynamic<'info, MangoAccount>,
pub account_a: AccountLoader<'info, MangoAccountFixed>,
// This account MUST have a loss
#[account(mut, has_one = group)]
pub account_b: AccountLoaderDynamic<'info, MangoAccount>,
pub account_b: AccountLoader<'info, MangoAccountFixed>,
/// CHECK: Oracle can have different account types, constrained by address in perp_market
pub oracle: UncheckedAccount<'info>,
@ -58,8 +57,8 @@ pub fn perp_settle_pnl(ctx: Context<PerpSettlePnl>) -> Result<()> {
)
};
let mut account_a = ctx.accounts.account_a.load_mut()?;
let mut account_b = ctx.accounts.account_b.load_mut()?;
let mut account_a = ctx.accounts.account_a.load_full_mut()?;
let mut account_b = ctx.accounts.account_b.load_full_mut()?;
// check positions exist, for nicer error messages
{
@ -223,7 +222,7 @@ pub fn perp_settle_pnl(ctx: Context<PerpSettlePnl>) -> Result<()> {
drop(account_a);
drop(account_b);
let mut settler = ctx.accounts.settler.load_mut()?;
let mut settler = ctx.accounts.settler.load_full_mut()?;
// account constraint #1
require!(
settler

View File

@ -14,7 +14,7 @@ pub struct Serum3CancelAllOrders<'info> {
has_one = group
// owner is checked at #1
)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(mut)]
@ -51,7 +51,7 @@ pub fn serum3_cancel_all_orders(ctx: Context<Serum3CancelAllOrders>, limit: u8)
// Validation
//
{
let account = ctx.accounts.account.load()?;
let account = ctx.accounts.account.load_full()?;
// account constraint #1
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),

View File

@ -20,7 +20,7 @@ pub struct Serum3CancelOrder<'info> {
has_one = group
// owner is checked at #1
)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(mut)]
@ -63,7 +63,7 @@ pub fn serum3_cancel_order(
// Validation
//
{
let account = ctx.accounts.account.load()?;
let account = ctx.accounts.account.load_full()?;
// account constraint #1
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),

View File

@ -12,7 +12,7 @@ pub struct Serum3CloseOpenOrders<'info> {
has_one = group
// owner is checked at #1
)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(
@ -39,7 +39,7 @@ pub fn serum3_close_open_orders(ctx: Context<Serum3CloseOpenOrders>) -> Result<(
//
// Validation
//
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
// account constraint #1
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),

View File

@ -12,7 +12,7 @@ pub struct Serum3CreateOpenOrders<'info> {
has_one = group
// owner is checked at #1
)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(
@ -51,7 +51,7 @@ pub fn serum3_create_open_orders(ctx: Context<Serum3CreateOpenOrders>) -> Result
let serum_market = ctx.accounts.serum_market.load()?;
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
// account constraint #1
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),

View File

@ -17,7 +17,7 @@ pub struct Serum3LiqForceCancelOrders<'info> {
pub group: AccountLoader<'info, Group>,
#[account(mut, has_one = group)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
#[account(mut)]
/// CHECK: Validated inline by checking against the pubkey stored in the account at #2
@ -77,7 +77,7 @@ pub fn serum3_liq_force_cancel_orders(
//
let serum_market = ctx.accounts.serum_market.load()?;
{
let account = ctx.accounts.account.load()?;
let account = ctx.accounts.account.load_full()?;
// Validate open_orders #2
require!(
@ -113,7 +113,7 @@ pub fn serum3_liq_force_cancel_orders(
// Check liqee health if liquidation is allowed
//
let mut health_cache = {
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
let retriever =
new_fixed_order_account_retriever(ctx.remaining_accounts, &account.borrow())?;
let health_cache =
@ -146,7 +146,7 @@ pub fn serum3_liq_force_cancel_orders(
let before_oo = {
let open_orders = load_open_orders_ref(ctx.accounts.open_orders.as_ref())?;
let before_oo = OpenOrdersSlim::from_oo(&open_orders);
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
charge_loan_origination_fees(
@ -208,7 +208,7 @@ pub fn serum3_liq_force_cancel_orders(
require_gte!(after_quote_vault, before_quote_vault);
// Credit the difference in vault balances to the user's account
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
apply_vault_difference(

View File

@ -142,7 +142,7 @@ pub struct Serum3PlaceOrder<'info> {
has_one = group
// owner is checked at #1
)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(mut)]
@ -217,7 +217,7 @@ pub fn serum3_place_order(
// Validation
//
{
let account = ctx.accounts.account.load()?;
let account = ctx.accounts.account.load_full()?;
// account constraint #1
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
@ -246,7 +246,7 @@ pub fn serum3_place_order(
//
// Pre-health computation
//
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
let pre_health_opt = if !account.fixed.is_in_health_region() {
let retriever =
new_fixed_order_account_retriever(ctx.remaining_accounts, &account.borrow())?;

View File

@ -20,7 +20,7 @@ pub struct Serum3SettleFunds<'info> {
has_one = group
// owner is checked at #1
)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(mut)]
@ -76,7 +76,7 @@ pub fn serum3_settle_funds(ctx: Context<Serum3SettleFunds>) -> Result<()> {
// Validation
//
{
let account = ctx.accounts.account.load()?;
let account = ctx.accounts.account.load_full()?;
// account constraint #1
require!(
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
@ -119,7 +119,7 @@ pub fn serum3_settle_funds(ctx: Context<Serum3SettleFunds>) -> Result<()> {
{
let open_orders = load_open_orders_ref(ctx.accounts.open_orders.as_ref())?;
let before_oo = OpenOrdersSlim::from_oo(&open_orders);
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
charge_loan_origination_fees(
@ -155,7 +155,7 @@ pub fn serum3_settle_funds(ctx: Context<Serum3SettleFunds>) -> Result<()> {
require_gte!(after_quote_vault, before_quote_vault);
// Credit the difference in vault balances to the user's account
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
apply_vault_difference(

View File

@ -18,7 +18,7 @@ pub struct TokenDepositIntoExisting<'info> {
pub group: AccountLoader<'info, Group>,
#[account(mut, has_one = group)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
#[account(
mut,
@ -48,7 +48,7 @@ pub struct TokenDeposit<'info> {
pub group: AccountLoader<'info, Group>,
#[account(mut, has_one = group, has_one = owner)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(
@ -76,7 +76,7 @@ pub struct TokenDeposit<'info> {
struct DepositCommon<'a, 'info> {
pub group: &'a AccountLoader<'info, Group>,
pub account: &'a AccountLoaderDynamic<'info, MangoAccount>,
pub account: &'a AccountLoader<'info, MangoAccountFixed>,
pub bank: &'a AccountLoader<'info, Bank>,
pub vault: &'a Account<'info, TokenAccount>,
pub oracle: &'a UncheckedAccount<'info>,
@ -107,7 +107,7 @@ impl<'a, 'info> DepositCommon<'a, 'info> {
let token_index = self.bank.load()?.token_index;
// Get the account's position for that token index
let mut account = self.account.load_mut()?;
let mut account = self.account.load_full_mut()?;
let (position, raw_token_index) = account.token_position_mut(token_index)?;
@ -190,7 +190,7 @@ impl<'a, 'info> DepositCommon<'a, 'info> {
pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
{
let token_index = ctx.accounts.bank.load()?.token_index;
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
account.ensure_token_position(token_index)?;
}

View File

@ -30,14 +30,14 @@ pub struct TokenLiqBankruptcy<'info> {
has_one = group
// liqor_owner is checked at #1
)]
pub liqor: AccountLoaderDynamic<'info, MangoAccount>,
pub liqor: AccountLoader<'info, MangoAccountFixed>,
pub liqor_owner: Signer<'info>,
#[account(
mut,
has_one = group
)]
pub liqee: AccountLoaderDynamic<'info, MangoAccount>,
pub liqee: AccountLoader<'info, MangoAccountFixed>,
#[account(
has_one = group,
@ -81,7 +81,7 @@ pub fn token_liq_bankruptcy(
let (bank_ais, health_ais) = &ctx.remaining_accounts.split_at(liab_mint_info.num_banks());
liab_mint_info.verify_banks_ais(bank_ais)?;
let mut liqor = ctx.accounts.liqor.load_mut()?;
let mut liqor = ctx.accounts.liqor.load_full_mut()?;
// account constraint #1
require!(
liqor
@ -93,7 +93,7 @@ pub fn token_liq_bankruptcy(
let mut account_retriever = ScanningAccountRetriever::new(health_ais, group_pk)?;
let mut liqee = ctx.accounts.liqee.load_mut()?;
let mut liqee = ctx.accounts.liqee.load_full_mut()?;
let mut liqee_health_cache = new_health_cache(&liqee.borrow(), &account_retriever)
.context("create liqee health cache")?;
require!(

View File

@ -20,14 +20,14 @@ pub struct TokenLiqWithToken<'info> {
has_one = group
// liqor_owner is checked at #1
)]
pub liqor: AccountLoaderDynamic<'info, MangoAccount>,
pub liqor: AccountLoader<'info, MangoAccountFixed>,
pub liqor_owner: Signer<'info>,
#[account(
mut,
has_one = group
)]
pub liqee: AccountLoaderDynamic<'info, MangoAccount>,
pub liqee: AccountLoader<'info, MangoAccountFixed>,
}
pub fn token_liq_with_token(
@ -42,7 +42,7 @@ pub fn token_liq_with_token(
let mut account_retriever = ScanningAccountRetriever::new(ctx.remaining_accounts, group_pk)
.context("create account retriever")?;
let mut liqor = ctx.accounts.liqor.load_mut()?;
let mut liqor = ctx.accounts.liqor.load_full_mut()?;
// account constraint #1
require!(
liqor
@ -52,7 +52,7 @@ pub fn token_liq_with_token(
);
require!(!liqor.fixed.being_liquidated(), MangoError::BeingLiquidated);
let mut liqee = ctx.accounts.liqee.load_mut()?;
let mut liqee = ctx.accounts.liqee.load_full_mut()?;
// Initial liqee health check
let mut liqee_health_cache = new_health_cache(&liqee.borrow(), &account_retriever)

View File

@ -18,7 +18,7 @@ pub struct TokenWithdraw<'info> {
pub group: AccountLoader<'info, Group>,
#[account(mut, has_one = group, has_one = owner)]
pub account: AccountLoaderDynamic<'info, MangoAccount>,
pub account: AccountLoader<'info, MangoAccountFixed>,
pub owner: Signer<'info>,
#[account(
@ -62,7 +62,7 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
let token_index = ctx.accounts.bank.load()?.token_index;
// Create the account's position for that token index
let mut account = ctx.accounts.account.load_mut()?;
let mut account = ctx.accounts.account.load_full_mut()?;
let (_, raw_token_index, _) = account.ensure_token_position(token_index)?;
// Health check _after_ the token position is guaranteed to exist

View File

@ -1,25 +1,15 @@
use std::cell::{Ref, RefMut};
use std::marker::PhantomData;
use std::mem::size_of;
use anchor_lang::prelude::*;
use anchor_lang::Discriminator;
use arrayref::array_ref;
// Header is created by scanning and parsing dynamic portion of the account
// Header stores useful information e.g. offsets to easily seek into dynamic content
/// Header is created by scanning and parsing the dynamic portion of the account.
/// This stores useful information e.g. offsets to easily seek into dynamic content.
pub trait DynamicHeader: Sized {
// build header by scanning and parsing dynamic portion of the account
fn from_bytes(data: &[u8]) -> Result<Self>;
/// Builds header by scanning and parsing the dynamic portion of the account.
fn from_bytes(dynamic_data: &[u8]) -> Result<Self>;
// initialize a header on a new account, if necessary
fn initialize(data: &mut [u8]) -> Result<()>;
}
pub trait DynamicAccountType: Owner + Discriminator {
type Header: DynamicHeader;
type Fixed: bytemuck::Pod;
/// initializes a header on the dynamic portion of a new account
fn initialize(dynamic_data: &mut [u8]) -> Result<()>;
}
#[derive(Clone)]
@ -29,19 +19,6 @@ pub struct DynamicAccount<Header, Fixed, Dynamic> {
pub dynamic: Dynamic,
}
pub type DynamicAccountValue<D> =
DynamicAccount<<D as DynamicAccountType>::Header, <D as DynamicAccountType>::Fixed, Vec<u8>>;
pub type DynamicAccountRef<'a, D> = DynamicAccount<
&'a <D as DynamicAccountType>::Header,
&'a <D as DynamicAccountType>::Fixed,
&'a [u8],
>;
pub type DynamicAccountRefMut<'a, D> = DynamicAccount<
&'a mut <D as DynamicAccountType>::Header,
&'a mut <D as DynamicAccountType>::Fixed,
&'a mut [u8],
>;
// Want to generalize over:
// - T (which is Borrow<T>)
// - &T (which is Borrow<T> and Deref<Target=T>)
@ -113,199 +90,3 @@ impl<T: Sized> DerefOrBorrowMut<[T]> for Vec<T> {
self
}
}
pub struct AccountLoaderDynamic<'info, D: DynamicAccountType> {
/// CHECK: is checked below
acc_info: AccountInfo<'info>,
phantom1: PhantomData<&'info D>,
}
impl<'info, D: DynamicAccountType> AccountLoaderDynamic<'info, D> {
pub fn try_from(acc_info: &AccountInfo<'info>) -> Result<Self> {
if acc_info.owner != &D::owner() {
return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
.with_pubkeys((*acc_info.owner, D::owner())));
}
let data = acc_info.try_borrow_data()?;
if data.len() < D::discriminator().len() {
return Err(ErrorCode::AccountDiscriminatorNotFound.into());
}
let disc_bytes = array_ref![data, 0, 8];
if disc_bytes != &D::discriminator() {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
Ok(Self {
acc_info: acc_info.clone(),
phantom1: PhantomData,
})
}
pub fn try_from_unchecked(acc_info: &AccountInfo<'info>) -> Result<Self> {
if acc_info.owner != &D::owner() {
return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram)
.with_pubkeys((*acc_info.owner, D::owner())));
}
Ok(Self {
acc_info: acc_info.clone(),
phantom1: PhantomData,
})
}
/// Returns a Ref to the account data structure for reading.
pub fn load_fixed(&self) -> Result<Ref<D::Fixed>> {
let data = self.acc_info.try_borrow_data()?;
let fixed = Ref::map(data, |d| {
bytemuck::from_bytes(&d[8..8 + size_of::<D::Fixed>()])
});
Ok(fixed)
}
#[allow(clippy::type_complexity)]
/// Returns a Ref to the account data structure for reading.
pub fn load(&self) -> Result<DynamicAccount<D::Header, Ref<D::Fixed>, Ref<[u8]>>> {
let data = self.acc_info.try_borrow_data()?;
let header = D::Header::from_bytes(&data[8 + size_of::<D::Fixed>()..])?;
let (_, data) = Ref::map_split(data, |d| d.split_at(8));
let (fixed_bytes, dynamic) = Ref::map_split(data, |d| d.split_at(size_of::<D::Fixed>()));
Ok(DynamicAccount {
header,
fixed: Ref::map(fixed_bytes, |b| bytemuck::from_bytes(b)),
dynamic,
})
}
#[allow(clippy::type_complexity)]
pub fn load_init(&self) -> Result<DynamicAccount<D::Header, RefMut<D::Fixed>, RefMut<[u8]>>> {
if !self.acc_info.is_writable {
return Err(ErrorCode::AccountNotMutable.into());
}
let mut data = self.acc_info.try_borrow_mut_data()?;
let mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
let discriminator = u64::from_le_bytes(disc_bytes);
if discriminator != 0 {
return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
}
let disc_bytes: &mut [u8] = &mut data[0..8];
disc_bytes.copy_from_slice(bytemuck::bytes_of(&(D::discriminator())));
D::Header::initialize(&mut data[8 + size_of::<D::Fixed>()..])?;
drop(data);
self.load_mut()
}
/// Returns a Ref to the account data structure for reading.
#[allow(clippy::type_complexity)]
pub fn load_mut(&self) -> Result<DynamicAccount<D::Header, RefMut<D::Fixed>, RefMut<[u8]>>> {
if !self.acc_info.is_writable {
return Err(ErrorCode::AccountNotMutable.into());
}
let data = self.acc_info.try_borrow_mut_data()?;
let header = D::Header::from_bytes(&data[8 + size_of::<D::Fixed>()..])?;
let (_, data) = RefMut::map_split(data, |d| d.split_at_mut(8));
let (fixed_bytes, dynamic) =
RefMut::map_split(data, |d| d.split_at_mut(size_of::<D::Fixed>()));
Ok(DynamicAccount {
header,
fixed: RefMut::map(fixed_bytes, |b| bytemuck::from_bytes_mut(b)),
dynamic,
})
}
}
impl<'info, D: DynamicAccountType> anchor_lang::Accounts<'info> for AccountLoaderDynamic<'info, D> {
#[inline(never)]
fn try_accounts(
_program_id: &Pubkey,
accounts: &mut &[AccountInfo<'info>],
_ix_data: &[u8],
_bumps: &mut std::collections::BTreeMap<String, u8>,
_reallocs: &mut std::collections::BTreeSet<Pubkey>,
) -> Result<Self> {
if accounts.is_empty() {
return Err(ErrorCode::AccountNotEnoughKeys.into());
}
let account = &accounts[0];
*accounts = &accounts[1..];
let l = AccountLoaderDynamic::try_from(account)?;
Ok(l)
}
}
impl<'info, D: DynamicAccountType> anchor_lang::AccountsExit<'info>
for AccountLoaderDynamic<'info, D>
{
fn exit(&self, _program_id: &Pubkey) -> Result<()> {
// Normally anchor writes the discriminator again here, but I don't see why
let data = self.acc_info.try_borrow_data()?;
if data.len() < D::discriminator().len() {
return Err(ErrorCode::AccountDiscriminatorNotFound.into());
}
let disc_bytes = array_ref![data, 0, 8];
if disc_bytes != &D::discriminator() {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
Ok(())
}
}
impl<'info, D: DynamicAccountType> anchor_lang::AccountsClose<'info>
for AccountLoaderDynamic<'info, D>
{
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
close(self.to_account_info(), sol_destination)
}
}
impl<'info, D: DynamicAccountType> anchor_lang::ToAccountMetas for AccountLoaderDynamic<'info, D> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
let is_signer = is_signer.unwrap_or(self.acc_info.is_signer);
let meta = match self.acc_info.is_writable {
false => AccountMeta::new_readonly(*self.acc_info.key, is_signer),
true => AccountMeta::new(*self.acc_info.key, is_signer),
};
vec![meta]
}
}
impl<'info, D: DynamicAccountType> AsRef<AccountInfo<'info>> for AccountLoaderDynamic<'info, D> {
fn as_ref(&self) -> &AccountInfo<'info> {
&self.acc_info
}
}
impl<'info, D: DynamicAccountType> anchor_lang::ToAccountInfos<'info>
for AccountLoaderDynamic<'info, D>
{
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
vec![self.acc_info.clone()]
}
}
impl<'info, D: DynamicAccountType> anchor_lang::Key for AccountLoaderDynamic<'info, D> {
fn key(&self) -> Pubkey {
*self.acc_info.key
}
}
// https://github.com/coral-xyz/anchor/blob/master/lang/src/common.rs#L8
fn close<'info>(info: AccountInfo<'info>, sol_destination: AccountInfo<'info>) -> Result<()> {
// Transfer tokens from the account to the sol_destination.
let dest_starting_lamports = sol_destination.lamports();
**sol_destination.lamports.borrow_mut() =
dest_starting_lamports.checked_add(info.lamports()).unwrap();
**info.lamports.borrow_mut() = 0;
// Mark the account discriminator as closed.
let mut data = info.try_borrow_mut_data()?;
let dst: &mut [u8] = &mut data;
dst[0..8].copy_from_slice(&[255, 255, 255, 255, 255, 255, 255, 255]);
Ok(())
}

View File

@ -1,6 +1,8 @@
use std::cell::{Ref, RefMut};
use std::mem::size_of;
use anchor_lang::prelude::*;
use anchor_lang::Discriminator;
use arrayref::array_ref;
use fixed::types::I80F48;
@ -248,11 +250,20 @@ impl MangoAccountFixed {
}
}
impl DynamicAccountType for MangoAccount {
type Header = MangoAccountDynamicHeader;
type Fixed = MangoAccountFixed;
impl Owner for MangoAccountFixed {
fn owner() -> Pubkey {
MangoAccount::owner()
}
}
impl Discriminator for MangoAccountFixed {
fn discriminator() -> [u8; 8] {
MangoAccount::discriminator()
}
}
impl anchor_lang::ZeroCopy for MangoAccountFixed {}
#[derive(Clone)]
pub struct MangoAccountDynamicHeader {
pub token_count: u8,
@ -262,34 +273,34 @@ pub struct MangoAccountDynamicHeader {
}
impl DynamicHeader for MangoAccountDynamicHeader {
fn from_bytes(data: &[u8]) -> Result<Self> {
let header_version = u8::from_le_bytes(*array_ref![data, 0, size_of::<u8>()]);
fn from_bytes(dynamic_data: &[u8]) -> Result<Self> {
let header_version = u8::from_le_bytes(*array_ref![dynamic_data, 0, size_of::<u8>()]);
match header_version {
1 => {
let token_count = u8::try_from(BorshVecLength::from_le_bytes(*array_ref![
data,
dynamic_data,
MangoAccount::dynamic_token_vec_offset(),
BORSH_VEC_SIZE_BYTES
]))
.unwrap();
let serum3_count = u8::try_from(BorshVecLength::from_le_bytes(*array_ref![
data,
dynamic_data,
MangoAccount::dynamic_serum3_vec_offset(token_count),
BORSH_VEC_SIZE_BYTES
]))
.unwrap();
let perp_count = u8::try_from(BorshVecLength::from_le_bytes(*array_ref![
data,
dynamic_data,
MangoAccount::dynamic_perp_vec_offset(token_count, serum3_count),
BORSH_VEC_SIZE_BYTES
]))
.unwrap();
let perp_oo_count = u8::try_from(BorshVecLength::from_le_bytes(*array_ref![
data,
dynamic_data,
MangoAccount::dynamic_perp_oo_vec_offset(token_count, serum3_count, perp_count),
BORSH_VEC_SIZE_BYTES
]))
@ -306,8 +317,8 @@ impl DynamicHeader for MangoAccountDynamicHeader {
}
}
fn initialize(data: &mut [u8]) -> Result<()> {
let dst: &mut [u8] = &mut data[0..1];
fn initialize(dynamic_data: &mut [u8]) -> Result<()> {
let dst: &mut [u8] = &mut dynamic_data[0..1];
dst.copy_from_slice(&DEFAULT_MANGO_ACCOUNT_VERSION.to_le_bytes());
Ok(())
}
@ -368,11 +379,25 @@ impl MangoAccountDynamicHeader {
}
}
pub type MangoAccountValue = DynamicAccountValue<MangoAccount>;
pub type MangoAccountRef<'a> = DynamicAccountRef<'a, MangoAccount>;
pub type MangoAccountRefMut<'a> = DynamicAccountRefMut<'a, MangoAccount>;
pub type MangoAccountRefWithHeader<'a> =
/// Fully owned MangoAccount, useful for tests
pub type MangoAccountValue = DynamicAccount<MangoAccountDynamicHeader, MangoAccountFixed, Vec<u8>>;
/// Full reference type, useful for borrows
pub type MangoAccountRef<'a> =
DynamicAccount<&'a MangoAccountDynamicHeader, &'a MangoAccountFixed, &'a [u8]>;
/// Full reference type, useful for borrows
pub type MangoAccountRefMut<'a> =
DynamicAccount<&'a mut MangoAccountDynamicHeader, &'a mut MangoAccountFixed, &'a mut [u8]>;
/// Useful when loading from bytes
pub type MangoAccountLoadedRef<'a> =
DynamicAccount<MangoAccountDynamicHeader, &'a MangoAccountFixed, &'a [u8]>;
/// Useful when loading from RefCell, like from AccountInfo
pub type MangoAccountLoadedRefCell<'a> =
DynamicAccount<MangoAccountDynamicHeader, Ref<'a, MangoAccountFixed>, Ref<'a, [u8]>>;
/// Useful when loading from RefCell, like from AccountInfo
pub type MangoAccountLoadedRefCellMut<'a> =
DynamicAccount<MangoAccountDynamicHeader, RefMut<'a, MangoAccountFixed>, RefMut<'a, [u8]>>;
impl MangoAccountValue {
// bytes without discriminator
@ -386,7 +411,7 @@ impl MangoAccountValue {
}
}
impl<'a> MangoAccountRefWithHeader<'a> {
impl<'a> MangoAccountLoadedRef<'a> {
// bytes without discriminator
pub fn from_bytes(bytes: &'a [u8]) -> Result<Self> {
let (fixed, dynamic) = bytes.split_at(size_of::<MangoAccountFixed>());
@ -399,7 +424,7 @@ impl<'a> MangoAccountRefWithHeader<'a> {
}
// This generic impl covers MangoAccountRef, MangoAccountRefMut and other
// DynamicAccountValue variants that allow read access.
// DynamicAccount variants that allow read access.
impl<
Header: DerefOrBorrow<MangoAccountDynamicHeader>,
Fixed: DerefOrBorrow<MangoAccountFixed>,
@ -541,8 +566,8 @@ impl<
self.fixed().being_liquidated()
}
pub fn borrow(&self) -> DynamicAccountRef<MangoAccount> {
DynamicAccount {
pub fn borrow(&self) -> MangoAccountRef {
MangoAccountRef {
header: self.header(),
fixed: self.fixed(),
dynamic: self.dynamic(),
@ -566,8 +591,8 @@ impl<
self.dynamic.deref_or_borrow_mut()
}
pub fn borrow_mut(&mut self) -> DynamicAccountRefMut<MangoAccount> {
DynamicAccount {
pub fn borrow_mut(&mut self) -> MangoAccountRefMut {
MangoAccountRefMut {
header: self.header.deref_or_borrow_mut(),
fixed: self.fixed.deref_or_borrow_mut(),
dynamic: self.dynamic.deref_or_borrow_mut(),
@ -1086,6 +1111,65 @@ impl<
}
}
/// Trait to allow a AccountLoader<MangoAccountFixed> to create an accessor for the full account.
pub trait MangoAccountLoader<'a> {
fn load_full(self) -> Result<MangoAccountLoadedRefCell<'a>>;
fn load_full_mut(self) -> Result<MangoAccountLoadedRefCellMut<'a>>;
fn load_full_init(self) -> Result<MangoAccountLoadedRefCellMut<'a>>;
}
impl<'a, 'info: 'a> MangoAccountLoader<'a> for &'a AccountLoader<'info, MangoAccountFixed> {
fn load_full(self) -> Result<MangoAccountLoadedRefCell<'a>> {
// Error checking
self.load()?;
let data = self.as_ref().try_borrow_data()?;
let header =
MangoAccountDynamicHeader::from_bytes(&data[8 + size_of::<MangoAccountFixed>()..])?;
let (_, data) = Ref::map_split(data, |d| d.split_at(8));
let (fixed_bytes, dynamic) =
Ref::map_split(data, |d| d.split_at(size_of::<MangoAccountFixed>()));
Ok(MangoAccountLoadedRefCell {
header,
fixed: Ref::map(fixed_bytes, |b| bytemuck::from_bytes(b)),
dynamic,
})
}
fn load_full_mut(self) -> Result<MangoAccountLoadedRefCellMut<'a>> {
// Error checking
self.load_mut()?;
let data = self.as_ref().try_borrow_mut_data()?;
let header =
MangoAccountDynamicHeader::from_bytes(&data[8 + size_of::<MangoAccountFixed>()..])?;
let (_, data) = RefMut::map_split(data, |d| d.split_at_mut(8));
let (fixed_bytes, dynamic) =
RefMut::map_split(data, |d| d.split_at_mut(size_of::<MangoAccountFixed>()));
Ok(MangoAccountLoadedRefCellMut {
header,
fixed: RefMut::map(fixed_bytes, |b| bytemuck::from_bytes_mut(b)),
dynamic,
})
}
fn load_full_init(self) -> Result<MangoAccountLoadedRefCellMut<'a>> {
// Error checking
self.load_init()?;
{
let mut data = self.as_ref().try_borrow_mut_data()?;
let disc_bytes: &mut [u8] = &mut data[0..8];
disc_bytes.copy_from_slice(bytemuck::bytes_of(&(MangoAccount::discriminator())));
MangoAccountDynamicHeader::initialize(&mut data[8 + size_of::<MangoAccountFixed>()..])?;
}
self.load_full_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;