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:
microwavedcola1 2023-01-23 14:58:03 +01:00 committed by GitHub
parent f0c797a2e4
commit 64dda20cb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 106 additions and 25 deletions

View File

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

View File

@ -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)?])
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"
}
]
};