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,
|
AccountIsFrozen,
|
||||||
#[msg("has open perp taker fills")]
|
#[msg("has open perp taker fills")]
|
||||||
HasOpenPerpTakerFills,
|
HasOpenPerpTakerFills,
|
||||||
|
#[msg("deposit crosses the current group deposit limit")]
|
||||||
|
DepositLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MangoError {
|
impl MangoError {
|
||||||
|
|
|
@ -346,6 +346,22 @@ impl HealthCache {
|
||||||
health
|
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> {
|
pub fn token_info(&self, token_index: TokenIndex) -> Result<&TokenInfo> {
|
||||||
Ok(&self.token_infos[self.token_info_index(token_index)?])
|
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
|
/// The health ratio is
|
||||||
/// - 0 if health is 0 - meaning assets = liabs
|
/// - 0 if health is 0 - meaning assets = liabs
|
||||||
/// - 100 if there's 2x as many assets as liabs
|
/// - 100 if there's 2x as many assets as liabs
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub fn group_edit(
|
||||||
security_admin_opt: Option<Pubkey>,
|
security_admin_opt: Option<Pubkey>,
|
||||||
testing_opt: Option<u8>,
|
testing_opt: Option<u8>,
|
||||||
version_opt: Option<u8>,
|
version_opt: Option<u8>,
|
||||||
|
deposit_limit_quote_opt: Option<u64>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut group = ctx.accounts.group.load_mut()?;
|
let mut group = ctx.accounts.group.load_mut()?;
|
||||||
|
|
||||||
|
@ -44,5 +45,9 @@ pub fn group_edit(
|
||||||
group.version = version;
|
group.version = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(deposit_limit_quote) = deposit_limit_quote_opt {
|
||||||
|
group.deposit_limit_quote = deposit_limit_quote;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,15 +186,15 @@ impl<'a, 'info> DepositCommon<'a, 'info> {
|
||||||
//
|
//
|
||||||
// Health computation
|
// 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.
|
// 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.
|
// Also, TokenDeposit is one of the rare instructions that is allowed even during being_liquidated.
|
||||||
//
|
//
|
||||||
if !account.fixed.is_in_health_region() {
|
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 was_being_liquidated = account.being_liquidated();
|
||||||
let recovered = account.fixed.maybe_recover_from_being_liquidated(health);
|
let recovered = account.fixed.maybe_recover_from_being_liquidated(health);
|
||||||
require!(
|
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
|
// 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
|
// 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>,
|
security_admin_opt: Option<Pubkey>,
|
||||||
testing_opt: Option<u8>,
|
testing_opt: Option<u8>,
|
||||||
version_opt: Option<u8>,
|
version_opt: Option<u8>,
|
||||||
|
deposit_limit_quote_opt: Option<u64>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
instructions::group_edit(
|
instructions::group_edit(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -56,6 +57,7 @@ pub mod mango_v4 {
|
||||||
security_admin_opt,
|
security_admin_opt,
|
||||||
testing_opt,
|
testing_opt,
|
||||||
version_opt,
|
version_opt,
|
||||||
|
deposit_limit_quote_opt,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,11 +39,15 @@ pub struct Group {
|
||||||
|
|
||||||
pub security_admin: Pubkey,
|
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!(
|
const_assert_eq!(
|
||||||
size_of::<Group>(),
|
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>(), 2736);
|
||||||
const_assert_eq!(size_of::<Group>() % 8, 0);
|
const_assert_eq!(size_of::<Group>() % 8, 0);
|
||||||
|
|
|
@ -134,6 +134,7 @@ export class MangoClient {
|
||||||
securityAdmin?: PublicKey,
|
securityAdmin?: PublicKey,
|
||||||
testing?: number,
|
testing?: number,
|
||||||
version?: number,
|
version?: number,
|
||||||
|
depositLimitQuote?: number,
|
||||||
): Promise<TransactionSignature> {
|
): Promise<TransactionSignature> {
|
||||||
return await this.program.methods
|
return await this.program.methods
|
||||||
.groupEdit(
|
.groupEdit(
|
||||||
|
@ -142,6 +143,7 @@ export class MangoClient {
|
||||||
securityAdmin ?? null,
|
securityAdmin ?? null,
|
||||||
testing ?? null,
|
testing ?? null,
|
||||||
version ?? null,
|
version ?? null,
|
||||||
|
depositLimitQuote !== undefined ? new BN(depositLimitQuote) : null,
|
||||||
)
|
)
|
||||||
.accounts({
|
.accounts({
|
||||||
group: group.publicKey,
|
group: group.publicKey,
|
||||||
|
|
|
@ -138,6 +138,12 @@ export type MangoV4 = {
|
||||||
"type": {
|
"type": {
|
||||||
"option": "u8"
|
"option": "u8"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "depositLimitQuoteOpt",
|
||||||
|
"type": {
|
||||||
|
"option": "u64"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -3982,12 +3988,16 @@ export type MangoV4 = {
|
||||||
"name": "securityAdmin",
|
"name": "securityAdmin",
|
||||||
"type": "publicKey"
|
"type": "publicKey"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "depositLimitQuote",
|
||||||
|
"type": "u64"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "reserved",
|
"name": "reserved",
|
||||||
"type": {
|
"type": {
|
||||||
"array": [
|
"array": [
|
||||||
"u8",
|
"u8",
|
||||||
1888
|
1880
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5086,6 +5096,10 @@ export type MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "hasOpenOrders",
|
"name": "hasOpenOrders",
|
||||||
"type": "bool"
|
"type": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hasOpenFills",
|
||||||
|
"type": "bool"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -7910,6 +7924,16 @@ export type MangoV4 = {
|
||||||
"code": 6038,
|
"code": 6038,
|
||||||
"name": "AccountIsFrozen",
|
"name": "AccountIsFrozen",
|
||||||
"msg": "account is frozen"
|
"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": {
|
"type": {
|
||||||
"option": "u8"
|
"option": "u8"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "depositLimitQuoteOpt",
|
||||||
|
"type": {
|
||||||
|
"option": "u64"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -11898,12 +11928,16 @@ export const IDL: MangoV4 = {
|
||||||
"name": "securityAdmin",
|
"name": "securityAdmin",
|
||||||
"type": "publicKey"
|
"type": "publicKey"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "depositLimitQuote",
|
||||||
|
"type": "u64"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "reserved",
|
"name": "reserved",
|
||||||
"type": {
|
"type": {
|
||||||
"array": [
|
"array": [
|
||||||
"u8",
|
"u8",
|
||||||
1888
|
1880
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13002,6 +13036,10 @@ export const IDL: MangoV4 = {
|
||||||
{
|
{
|
||||||
"name": "hasOpenOrders",
|
"name": "hasOpenOrders",
|
||||||
"type": "bool"
|
"type": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hasOpenFills",
|
||||||
|
"type": "bool"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -15826,6 +15864,16 @@ export const IDL: MangoV4 = {
|
||||||
"code": 6038,
|
"code": 6038,
|
||||||
"name": "AccountIsFrozen",
|
"name": "AccountIsFrozen",
|
||||||
"msg": "account is frozen"
|
"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