diff --git a/programs/mango-v4/src/error.rs b/programs/mango-v4/src/error.rs index 01fbba024f..181f283a60 100644 --- a/programs/mango-v4/src/error.rs +++ b/programs/mango-v4/src/error.rs @@ -69,6 +69,8 @@ pub enum MangoError { TokenInReduceOnlyMode, #[msg("market is in reduce only mode")] MarketInReduceOnlyMode, + #[msg("group is halted")] + GroupIsHalted, #[msg("the perp position has non-zero base lots")] PerpHasBaseLots, #[msg("there are open or unsettled serum3 orders")] @@ -87,7 +89,7 @@ pub enum MangoError { HasOpenPerpTakerFills, #[msg("deposit crosses the current group deposit limit")] DepositLimit, - #[msg("ix is disabled")] + #[msg("instruction is disabled")] IxIsDisabled, } diff --git a/programs/mango-v4/src/instructions/health_region.rs b/programs/mango-v4/src/instructions/health_region.rs index 65318ae5ee..7563bd110f 100644 --- a/programs/mango-v4/src/instructions/health_region.rs +++ b/programs/mango-v4/src/instructions/health_region.rs @@ -18,8 +18,14 @@ pub struct HealthRegionBegin<'info> { #[account(address = tx_instructions::ID)] pub instructions: UncheckedAccount<'info>, + #[account( + constraint = group.load()?.is_ix_enabled(IxGate::HealthRegion) @ MangoError::IxIsDisabled, + )] + pub group: AccountLoader<'info, Group>, + #[account( mut, + has_one = group, constraint = account.load()?.is_operational() @ MangoError::AccountIsFrozen )] pub account: AccountLoader<'info, MangoAccountFixed>, @@ -30,8 +36,14 @@ pub struct HealthRegionBegin<'info> { /// remaining_accounts: health accounts for account #[derive(Accounts)] pub struct HealthRegionEnd<'info> { + #[account( + constraint = group.load()?.is_ix_enabled(IxGate::HealthRegion) @ MangoError::IxIsDisabled, + )] + pub group: AccountLoader<'info, Group>, + #[account( mut, + has_one = group, constraint = account.load()?.is_operational() @ MangoError::AccountIsFrozen )] pub account: AccountLoader<'info, MangoAccountFixed>, diff --git a/programs/mango-v4/src/instructions/ix_gate_set.rs b/programs/mango-v4/src/instructions/ix_gate_set.rs index 6bdd3a4eab..65120ec1c6 100644 --- a/programs/mango-v4/src/instructions/ix_gate_set.rs +++ b/programs/mango-v4/src/instructions/ix_gate_set.rs @@ -1,12 +1,12 @@ use anchor_lang::prelude::*; -use crate::state::*; +use crate::{error::MangoError, state::*}; #[derive(Accounts)] pub struct IxGateSet<'info> { #[account( mut, - has_one = admin, + // group <-> admin relation is checked at #1 )] pub group: AccountLoader<'info, Group>, pub admin: Signer<'info>, @@ -14,7 +14,31 @@ pub struct IxGateSet<'info> { pub fn ix_gate_set(ctx: Context, ix_gate: u128) -> Result<()> { let mut group = ctx.accounts.group.load_mut()?; + msg!("old {:?}, new {:?}", group.ix_gate, ix_gate); + + let mut require_group_admin = false; + for i in 0..=47 { + // only admin can re-enable + if group.ix_gate & (1 << i) == 1 && ix_gate & (1 << i) == 0 { + require_group_admin = true; + } + } + group.ix_gate = ix_gate; + + // account constraint #1 + if require_group_admin { + require!( + group.admin == ctx.accounts.admin.key(), + MangoError::SomeError + ); + } else { + require!( + group.admin == ctx.accounts.admin.key() + || group.security_admin == ctx.accounts.admin.key(), + MangoError::SomeError + ); + } Ok(()) } diff --git a/programs/mango-v4/src/state/group.rs b/programs/mango-v4/src/state/group.rs index f6c948898d..8a72988b2a 100644 --- a/programs/mango-v4/src/state/group.rs +++ b/programs/mango-v4/src/state/group.rs @@ -79,65 +79,65 @@ impl Group { /// Enum for lookup into ix gate /// note: /// total ix files 56, -/// ix files included 47, -/// ix files not included 9, +/// ix files included 48, +/// ix files not included 8, /// - Benchmark, /// - ComputeAccountData, /// - GroupCreate /// - GroupEdit -/// - HealthRegion /// - IxGateSet, /// - PerpZeroOut, /// - PerpEditMarket, /// - TokenEdit, pub enum IxGate { AccountClose = 0, - AccountCreate, - AccountEdit, - AccountExpand, - AccountToggleFreeze, - AltExtend, - AltSet, - FlashLoan, - GroupClose, - GroupCreate, - PerpCancelAllOrders = 10, - PerpCancelAllOrdersBySide, - PerpCancelOrder, - PerpCancelOrderByClientOrderId, - PerpCloseMarket, - PerpConsumeEvents, - PerpCreateMarket, - PerpDeactivatePosition, - PerpLiqBasePosition, - PerpLiqForceCancelOrders, - PerpLiqQuoteAndBankruptcy = 20, - PerpPlaceOrder, - PerpSettleFees, - PerpSettlePnl, - PerpUpdateFunding, - Serum3CancelAllOrders, - Serum3CancelOrder, - Serum3CloseOpenOrders, - Serum3CreateOpenOrders, - Serum3DeregisterMarket, - Serum3EditMarket = 30, - Serum3LiqForceCancelOrders, - Serum3PlaceOrder, - Serum3RegisterMarket, - Serum3SettleFunds, - StubOracleClose, - StubOracleCreate, - StubOracleSet, - TokenAddBank, - TokenDeposit, - TokenDeregister = 40, - TokenLiqBankruptcy, - TokenLiqWithToken, - TokenRegister, - TokenRegisterTrustless, - TokenUpdateIndexAndRate, - TokenWithdraw, + AccountCreate = 1, + AccountEdit = 2, + AccountExpand = 3, + AccountToggleFreeze = 4, + AltExtend = 5, + AltSet = 6, + FlashLoan = 7, + GroupClose = 8, + GroupCreate = 9, + HealthRegion = 10, + PerpCancelAllOrders = 11, + PerpCancelAllOrdersBySide = 12, + PerpCancelOrder = 13, + PerpCancelOrderByClientOrderId = 14, + PerpCloseMarket = 15, + PerpConsumeEvents = 16, + PerpCreateMarket = 17, + PerpDeactivatePosition = 18, + PerpLiqBasePosition = 19, + PerpLiqForceCancelOrders = 20, + PerpLiqQuoteAndBankruptcy = 21, + PerpPlaceOrder = 22, + PerpSettleFees = 23, + PerpSettlePnl = 24, + PerpUpdateFunding = 25, + Serum3CancelAllOrders = 26, + Serum3CancelOrder = 27, + Serum3CloseOpenOrders = 28, + Serum3CreateOpenOrders = 29, + Serum3DeregisterMarket = 30, + Serum3EditMarket = 31, + Serum3LiqForceCancelOrders = 32, + Serum3PlaceOrder = 33, + Serum3RegisterMarket = 34, + Serum3SettleFunds = 35, + StubOracleClose = 36, + StubOracleCreate = 37, + StubOracleSet = 38, + TokenAddBank = 39, + TokenDeposit = 40, + TokenDeregister = 41, + TokenLiqBankruptcy = 42, + TokenLiqWithToken = 43, + TokenRegister = 44, + TokenRegisterTrustless = 45, + TokenUpdateIndexAndRate = 46, + TokenWithdraw = 47, } // note: using creator instead of admin, since admin can be changed diff --git a/programs/mango-v4/tests/test_ix_gate_set.rs b/programs/mango-v4/tests/test_ix_gate_set.rs index 2cff706e3f..39cdaf2135 100644 --- a/programs/mango-v4/tests/test_ix_gate_set.rs +++ b/programs/mango-v4/tests/test_ix_gate_set.rs @@ -34,22 +34,16 @@ async fn test_ix_gate_set() -> Result<(), TransportError> { .create(solana) .await; - let account = send_tx( - solana, - AccountCreateInstruction { - account_num: 0, - token_count: 8, - serum3_count: 7, - perp_count: 0, - perp_oo_count: 0, - group, - owner, - payer, - }, - ) - .await - .unwrap() - .account; + let account = create_funded_account( + &solana, + group, + owner, + 0, + &context.users[1], + &mints[0..1], + initial_token_deposit, + 0, + ); send_tx( solana, diff --git a/ts/client/src/clientIxParamBuilder.ts b/ts/client/src/clientIxParamBuilder.ts index 06a8db3fb7..08ca54efae 100644 --- a/ts/client/src/clientIxParamBuilder.ts +++ b/ts/client/src/clientIxParamBuilder.ts @@ -225,59 +225,66 @@ export const TrueIxGateParams: IxGateParams = { // build ix gate e.g. buildIxGate(Builder(TrueIxGateParams).TokenDeposit(false).build()).toNumber(), export function buildIxGate(p: IxGateParams): BN { const ixGate = new BN(0); - ixGate.ior(p.AccountClose ? new BN(0) : new BN(1).ushln(0)); - ixGate.ior(p.AccountCreate ? new BN(0) : new BN(1).ushln(1)); - ixGate.ior(p.AccountEdit ? new BN(0) : new BN(1).ushln(2)); - ixGate.ior(p.AccountExpand ? new BN(0) : new BN(1).ushln(3)); - ixGate.ior(p.AccountToggleFreeze ? new BN(0) : new BN(1).ushln(4)); - ixGate.ior(p.AltExtend ? new BN(0) : new BN(1).ushln(5)); - ixGate.ior(p.AltSet ? new BN(0) : new BN(1).ushln(6)); - ixGate.ior(p.FlashLoan ? new BN(0) : new BN(1).ushln(7)); - ixGate.ior(p.GroupClose ? new BN(0) : new BN(1).ushln(8)); - ixGate.ior(p.GroupCreate ? new BN(0) : new BN(1).ushln(9)); - ixGate.ior(p.PerpCancelAllOrders ? new BN(0) : new BN(1).ushln(10)); - ixGate.ior(p.PerpCancelAllOrdersBySide ? new BN(0) : new BN(1).ushln(11)); - ixGate.ior(p.PerpCancelOrder ? new BN(0) : new BN(1).ushln(12)); - ixGate.ior( - p.PerpCancelOrderByClientOrderId ? new BN(0) : new BN(1).ushln(13), - ); - ixGate.ior(p.PerpCloseMarket ? new BN(0) : new BN(1).ushln(14)); - ixGate.ior(p.PerpConsumeEvents ? new BN(0) : new BN(1).ushln(15)); - ixGate.ior(p.PerpCreateMarket ? new BN(0) : new BN(1).ushln(16)); - ixGate.ior(p.PerpDeactivatePosition ? new BN(0) : new BN(1).ushln(17)); - ixGate.ior(p.PerpLiqBasePosition ? new BN(0) : new BN(1).ushln(18)); - ixGate.ior(p.PerpLiqForceCancelOrders ? new BN(0) : new BN(1).ushln(19)); - - ixGate.ior(p.PerpLiqQuoteAndBankruptcy ? new BN(0) : new BN(1).ushln(20)); - ixGate.ior(p.PerpPlaceOrder ? new BN(0) : new BN(1).ushln(21)); - ixGate.ior(p.PerpSettleFees ? new BN(0) : new BN(1).ushln(22)); - ixGate.ior(p.PerpSettlePnl ? new BN(0) : new BN(1).ushln(23)); - ixGate.ior(p.PerpUpdateFunding ? new BN(0) : new BN(1).ushln(24)); - ixGate.ior(p.Serum3CancelAllOrders ? new BN(0) : new BN(1).ushln(25)); - ixGate.ior(p.Serum3CancelOrder ? new BN(0) : new BN(1).ushln(26)); - ixGate.ior(p.Serum3CloseOpenOrders ? new BN(0) : new BN(1).ushln(27)); - ixGate.ior(p.Serum3CreateOpenOrders ? new BN(0) : new BN(1).ushln(28)); - ixGate.ior(p.Serum3DeregisterMarket ? new BN(0) : new BN(1).ushln(29)); - - ixGate.ior(p.Serum3EditMarket ? new BN(0) : new BN(1).ushln(30)); - ixGate.ior(p.Serum3LiqForceCancelOrders ? new BN(0) : new BN(1).ushln(31)); - ixGate.ior(p.Serum3PlaceOrder ? new BN(0) : new BN(1).ushln(32)); - ixGate.ior(p.Serum3RegisterMarket ? new BN(0) : new BN(1).ushln(33)); - ixGate.ior(p.Serum3SettleFunds ? new BN(0) : new BN(1).ushln(34)); - ixGate.ior(p.StubOracleClose ? new BN(0) : new BN(1).ushln(35)); - ixGate.ior(p.StubOracleCreate ? new BN(0) : new BN(1).ushln(36)); - ixGate.ior(p.StubOracleSet ? new BN(0) : new BN(1).ushln(37)); - ixGate.ior(p.TokenAddBank ? new BN(0) : new BN(1).ushln(38)); - ixGate.ior(p.TokenDeposit ? new BN(0) : new BN(1).ushln(39)); - - ixGate.ior(p.TokenDeregister ? new BN(0) : new BN(1).ushln(40)); - ixGate.ior(p.TokenLiqBankruptcy ? new BN(0) : new BN(1).ushln(41)); - ixGate.ior(p.TokenLiqWithToken ? new BN(0) : new BN(1).ushln(42)); - ixGate.ior(p.TokenRegister ? new BN(0) : new BN(1).ushln(43)); - ixGate.ior(p.TokenRegisterTrustless ? new BN(0) : new BN(1).ushln(44)); - ixGate.ior(p.TokenUpdateIndexAndRate ? new BN(0) : new BN(1).ushln(45)); - ixGate.ior(p.TokenWithdraw ? new BN(0) : new BN(1).ushln(46)); + function toggleIx( + ixGate: BN, + p: IxGateParams, + propName: string, + index: number, + ): void { + if (p[propName] === undefined) { + throw new Error(`Unknown property ${propName}`); + } + ixGate.ior(p[propName] ? new BN(0) : new BN(1).ushln(index)); + } + toggleIx(ixGate, p, 'AccountClose', 0); + toggleIx(ixGate, p, 'AccountCreate', 1); + toggleIx(ixGate, p, 'AccountEdit', 2); + toggleIx(ixGate, p, 'AccountExpand', 3); + toggleIx(ixGate, p, 'AccountToggleFreeze', 4); + toggleIx(ixGate, p, 'AltExtend', 5); + toggleIx(ixGate, p, 'AltSet', 6); + toggleIx(ixGate, p, 'FlashLoan', 7); + toggleIx(ixGate, p, 'GroupClose', 8); + toggleIx(ixGate, p, 'GroupCreate', 9); + toggleIx(ixGate, p, 'HealthRegion', 10); + toggleIx(ixGate, p, 'PerpCancelAllOrders', 11); + toggleIx(ixGate, p, 'PerpCancelAllOrdersBySide', 12); + toggleIx(ixGate, p, 'PerpCancelOrder', 13); + toggleIx(ixGate, p, 'PerpCancelOrderByClientOrderId', 14); + toggleIx(ixGate, p, 'PerpCloseMarket', 15); + toggleIx(ixGate, p, 'PerpConsumeEvents', 16); + toggleIx(ixGate, p, 'PerpCreateMarket', 17); + toggleIx(ixGate, p, 'PerpDeactivatePosition', 18); + toggleIx(ixGate, p, 'PerpLiqBasePosition', 19); + toggleIx(ixGate, p, 'PerpLiqForceCancelOrders', 20); + toggleIx(ixGate, p, 'PerpLiqQuoteAndBankruptcy', 21); + toggleIx(ixGate, p, 'PerpPlaceOrder', 22); + toggleIx(ixGate, p, 'PerpSettleFees', 23); + toggleIx(ixGate, p, 'PerpSettlePnl', 24); + toggleIx(ixGate, p, 'PerpUpdateFunding', 25); + toggleIx(ixGate, p, 'Serum3CancelAllOrders', 26); + toggleIx(ixGate, p, 'Serum3CancelOrder', 27); + toggleIx(ixGate, p, 'Serum3CloseOpenOrders', 28); + toggleIx(ixGate, p, 'Serum3CreateOpenOrders', 29); + toggleIx(ixGate, p, 'Serum3DeregisterMarket', 30); + toggleIx(ixGate, p, 'Serum3EditMarket', 31); + toggleIx(ixGate, p, 'Serum3LiqForceCancelOrders', 32); + toggleIx(ixGate, p, 'Serum3PlaceOrder', 33); + toggleIx(ixGate, p, 'Serum3RegisterMarket', 34); + toggleIx(ixGate, p, 'Serum3SettleFunds', 35); + toggleIx(ixGate, p, 'StubOracleClose', 36); + toggleIx(ixGate, p, 'StubOracleCreate', 37); + toggleIx(ixGate, p, 'StubOracleSet', 38); + toggleIx(ixGate, p, 'TokenAddBank', 39); + toggleIx(ixGate, p, 'TokenDeposit', 40); + toggleIx(ixGate, p, 'TokenDeregister', 41); + toggleIx(ixGate, p, 'TokenLiqBankruptcy', 42); + toggleIx(ixGate, p, 'TokenLiqWithToken', 43); + toggleIx(ixGate, p, 'TokenRegister', 44); + toggleIx(ixGate, p, 'TokenRegisterTrustless', 45); + toggleIx(ixGate, p, 'TokenUpdateIndexAndRate', 46); + toggleIx(ixGate, p, 'TokenWithdraw', 47); return ixGate; }