group level token deposit limit (#415)
* group level token deposit limit Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * fix Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * fix Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * fix Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
f0c797a2e4
commit
64dda20cb5
|
@ -85,6 +85,8 @@ pub enum MangoError {
|
|||
AccountIsFrozen,
|
||||
#[msg("has open perp taker fills")]
|
||||
HasOpenPerpTakerFills,
|
||||
#[msg("deposit crosses the current group deposit limit")]
|
||||
DepositLimit,
|
||||
}
|
||||
|
||||
impl MangoError {
|
||||
|
|
|
@ -346,6 +346,22 @@ impl HealthCache {
|
|||
health
|
||||
}
|
||||
|
||||
/// Sum of only the positive health components (assets) and
|
||||
/// sum of absolute values of all negative health components (liabs, always >= 0)
|
||||
pub fn health_assets_and_liabs(&self, health_type: HealthType) -> (I80F48, I80F48) {
|
||||
let mut assets = I80F48::ZERO;
|
||||
let mut liabs = I80F48::ZERO;
|
||||
let sum = |contrib| {
|
||||
if contrib > 0 {
|
||||
cm!(assets += contrib);
|
||||
} else {
|
||||
cm!(liabs -= contrib);
|
||||
}
|
||||
};
|
||||
self.health_sum(health_type, sum);
|
||||
(assets, liabs)
|
||||
}
|
||||
|
||||
pub fn token_info(&self, token_index: TokenIndex) -> Result<&TokenInfo> {
|
||||
Ok(&self.token_infos[self.token_info_index(token_index)?])
|
||||
}
|
||||
|
|
|
@ -21,22 +21,6 @@ impl HealthCache {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sum of only the positive health components (assets) and
|
||||
/// sum of absolute values of all negative health components (liabs, always >= 0)
|
||||
pub fn health_assets_and_liabs(&self, health_type: HealthType) -> (I80F48, I80F48) {
|
||||
let mut assets = I80F48::ZERO;
|
||||
let mut liabs = I80F48::ZERO;
|
||||
let sum = |contrib| {
|
||||
if contrib > 0 {
|
||||
cm!(assets += contrib);
|
||||
} else {
|
||||
cm!(liabs -= contrib);
|
||||
}
|
||||
};
|
||||
self.health_sum(health_type, sum);
|
||||
(assets, liabs)
|
||||
}
|
||||
|
||||
/// The health ratio is
|
||||
/// - 0 if health is 0 - meaning assets = liabs
|
||||
/// - 100 if there's 2x as many assets as liabs
|
||||
|
|
|
@ -21,6 +21,7 @@ pub fn group_edit(
|
|||
security_admin_opt: Option<Pubkey>,
|
||||
testing_opt: Option<u8>,
|
||||
version_opt: Option<u8>,
|
||||
deposit_limit_quote_opt: Option<u64>,
|
||||
) -> Result<()> {
|
||||
let mut group = ctx.accounts.group.load_mut()?;
|
||||
|
||||
|
@ -44,5 +45,9 @@ pub fn group_edit(
|
|||
group.version = version;
|
||||
}
|
||||
|
||||
if let Some(deposit_limit_quote) = deposit_limit_quote_opt {
|
||||
group.deposit_limit_quote = deposit_limit_quote;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -186,15 +186,15 @@ impl<'a, 'info> DepositCommon<'a, 'info> {
|
|||
//
|
||||
// Health computation
|
||||
//
|
||||
let retriever = new_fixed_order_account_retriever(remaining_accounts, &account.borrow())?;
|
||||
let cache = new_health_cache(&account.borrow(), &retriever)?;
|
||||
let health = cache.health(HealthType::Init);
|
||||
msg!("health: {}", health);
|
||||
|
||||
// Since depositing can only increase health, we can skip the usual pre-health computation.
|
||||
// Also, TokenDeposit is one of the rare instructions that is allowed even during being_liquidated.
|
||||
//
|
||||
if !account.fixed.is_in_health_region() {
|
||||
let retriever =
|
||||
new_fixed_order_account_retriever(remaining_accounts, &account.borrow())?;
|
||||
let health = compute_health(&account.borrow(), HealthType::Init, &retriever)
|
||||
.context("post-deposit init health")?;
|
||||
msg!("health: {}", health);
|
||||
let was_being_liquidated = account.being_liquidated();
|
||||
let recovered = account.fixed.maybe_recover_from_being_liquidated(health);
|
||||
require!(
|
||||
|
@ -203,6 +203,24 @@ impl<'a, 'info> DepositCommon<'a, 'info> {
|
|||
);
|
||||
}
|
||||
|
||||
// Group level deposit limit on account
|
||||
let assets = cache
|
||||
.health_assets_and_liabs(HealthType::Init)
|
||||
.0
|
||||
.round_to_zero()
|
||||
.checked_to_num::<u64>()
|
||||
.unwrap();
|
||||
let group = self.group.load()?;
|
||||
if group.deposit_limit_quote > 0 && assets > group.deposit_limit_quote {
|
||||
require_msg_typed!(
|
||||
assets <= group.deposit_limit_quote,
|
||||
MangoError::DepositLimit,
|
||||
"assets ({}) can't cross deposit limit on the group ({})",
|
||||
assets,
|
||||
group.deposit_limit_quote
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Deactivate the position only after the health check because the user passed in
|
||||
// remaining_accounts for all banks/oracles, including the account that will now be
|
||||
|
|
|
@ -48,6 +48,7 @@ pub mod mango_v4 {
|
|||
security_admin_opt: Option<Pubkey>,
|
||||
testing_opt: Option<u8>,
|
||||
version_opt: Option<u8>,
|
||||
deposit_limit_quote_opt: Option<u64>,
|
||||
) -> Result<()> {
|
||||
instructions::group_edit(
|
||||
ctx,
|
||||
|
@ -56,6 +57,7 @@ pub mod mango_v4 {
|
|||
security_admin_opt,
|
||||
testing_opt,
|
||||
version_opt,
|
||||
deposit_limit_quote_opt,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -39,11 +39,15 @@ pub struct Group {
|
|||
|
||||
pub security_admin: Pubkey,
|
||||
|
||||
pub reserved: [u8; 1888],
|
||||
// Deposit limit for a mango account in quote native, enforced on quote value of account assets
|
||||
// Set to 0 to disable, which also means by default there is no limit
|
||||
pub deposit_limit_quote: u64,
|
||||
|
||||
pub reserved: [u8; 1880],
|
||||
}
|
||||
const_assert_eq!(
|
||||
size_of::<Group>(),
|
||||
32 + 4 + 32 * 2 + 4 + 32 * 2 + 4 + 4 + 20 * 32 + 32 + 1888
|
||||
32 + 4 + 32 * 2 + 4 + 32 * 2 + 4 + 4 + 20 * 32 + 32 + 8 + 1880
|
||||
);
|
||||
const_assert_eq!(size_of::<Group>(), 2736);
|
||||
const_assert_eq!(size_of::<Group>() % 8, 0);
|
||||
|
|
|
@ -134,6 +134,7 @@ export class MangoClient {
|
|||
securityAdmin?: PublicKey,
|
||||
testing?: number,
|
||||
version?: number,
|
||||
depositLimitQuote?: number,
|
||||
): Promise<TransactionSignature> {
|
||||
return await this.program.methods
|
||||
.groupEdit(
|
||||
|
@ -142,6 +143,7 @@ export class MangoClient {
|
|||
securityAdmin ?? null,
|
||||
testing ?? null,
|
||||
version ?? null,
|
||||
depositLimitQuote !== undefined ? new BN(depositLimitQuote) : null,
|
||||
)
|
||||
.accounts({
|
||||
group: group.publicKey,
|
||||
|
|
|
@ -138,6 +138,12 @@ export type MangoV4 = {
|
|||
"type": {
|
||||
"option": "u8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "depositLimitQuoteOpt",
|
||||
"type": {
|
||||
"option": "u64"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -3982,12 +3988,16 @@ export type MangoV4 = {
|
|||
"name": "securityAdmin",
|
||||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "depositLimitQuote",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "reserved",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
1888
|
||||
1880
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -5086,6 +5096,10 @@ export type MangoV4 = {
|
|||
{
|
||||
"name": "hasOpenOrders",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "hasOpenFills",
|
||||
"type": "bool"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -7910,6 +7924,16 @@ export type MangoV4 = {
|
|||
"code": 6038,
|
||||
"name": "AccountIsFrozen",
|
||||
"msg": "account is frozen"
|
||||
},
|
||||
{
|
||||
"code": 6039,
|
||||
"name": "HasOpenPerpTakerFills",
|
||||
"msg": "has open perp taker fills"
|
||||
},
|
||||
{
|
||||
"code": 6040,
|
||||
"name": "DepositLimit",
|
||||
"msg": "deposit crosses the current group deposit limit"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
@ -8054,6 +8078,12 @@ export const IDL: MangoV4 = {
|
|||
"type": {
|
||||
"option": "u8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "depositLimitQuoteOpt",
|
||||
"type": {
|
||||
"option": "u64"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -11898,12 +11928,16 @@ export const IDL: MangoV4 = {
|
|||
"name": "securityAdmin",
|
||||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "depositLimitQuote",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "reserved",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
1888
|
||||
1880
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -13002,6 +13036,10 @@ export const IDL: MangoV4 = {
|
|||
{
|
||||
"name": "hasOpenOrders",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "hasOpenFills",
|
||||
"type": "bool"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -15826,6 +15864,16 @@ export const IDL: MangoV4 = {
|
|||
"code": 6038,
|
||||
"name": "AccountIsFrozen",
|
||||
"msg": "account is frozen"
|
||||
},
|
||||
{
|
||||
"code": 6039,
|
||||
"name": "HasOpenPerpTakerFills",
|
||||
"msg": "has open perp taker fills"
|
||||
},
|
||||
{
|
||||
"code": 6040,
|
||||
"name": "DepositLimit",
|
||||
"msg": "deposit crosses the current group deposit limit"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue