From 40a011a48e2c017ebc79db8d7c42e45e7c831cc4 Mon Sep 17 00:00:00 2001 From: microwavedcola1 <89031858+microwavedcola1@users.noreply.github.com> Date: Thu, 13 Jul 2023 18:22:35 +0200 Subject: [PATCH] Fees to dao (#644) * withdraw fees to dao Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * rename Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 --------- Signed-off-by: microwavedcola1 --- .../accounts_ix/admin_perp_withdraw_fees.rs | 52 +++++++++++++++++++ .../accounts_ix/admin_token_withdraw_fees.rs | 45 ++++++++++++++++ programs/mango-v4/src/accounts_ix/mod.rs | 4 ++ .../instructions/admin_perp_withdraw_fees.rs | 21 ++++++++ .../instructions/admin_withdraw_token_fees.rs | 21 ++++++++ .../mango-v4/src/instructions/ix_gate_set.rs | 12 +++++ programs/mango-v4/src/instructions/mod.rs | 4 ++ .../instructions/token_register_trustless.rs | 4 +- programs/mango-v4/src/lib.rs | 12 +++++ programs/mango-v4/src/state/bank.rs | 16 ++++-- programs/mango-v4/src/state/group.rs | 2 + programs/mango-v4/src/state/perp_market.rs | 14 +++-- ts/client/src/clientIxParamBuilder.ts | 36 +++++++++++++ 13 files changed, 236 insertions(+), 7 deletions(-) create mode 100644 programs/mango-v4/src/accounts_ix/admin_perp_withdraw_fees.rs create mode 100644 programs/mango-v4/src/accounts_ix/admin_token_withdraw_fees.rs create mode 100644 programs/mango-v4/src/instructions/admin_perp_withdraw_fees.rs create mode 100644 programs/mango-v4/src/instructions/admin_withdraw_token_fees.rs diff --git a/programs/mango-v4/src/accounts_ix/admin_perp_withdraw_fees.rs b/programs/mango-v4/src/accounts_ix/admin_perp_withdraw_fees.rs new file mode 100644 index 000000000..3b3db877d --- /dev/null +++ b/programs/mango-v4/src/accounts_ix/admin_perp_withdraw_fees.rs @@ -0,0 +1,52 @@ +use anchor_lang::prelude::*; +use anchor_spl::token; +use anchor_spl::token::Token; +use anchor_spl::token::TokenAccount; + +use crate::error::*; +use crate::state::*; + +#[derive(Accounts)] +pub struct AdminPerpWithdrawFees<'info> { + #[account( + constraint = group.load()?.is_ix_enabled(IxGate::AdminPerpWithdrawFees) @ MangoError::IxIsDisabled, + has_one = admin, + )] + pub group: AccountLoader<'info, Group>, + + #[account( + mut, + has_one = group, + )] + pub perp_market: AccountLoader<'info, PerpMarket>, + + #[account( + mut, + has_one = group, + has_one = vault, + constraint = bank.load()?.token_index == perp_market.load()?.settle_token_index + )] + pub bank: AccountLoader<'info, Bank>, + + #[account(mut)] + pub vault: Account<'info, TokenAccount>, + + #[account(mut)] + pub token_account: Box>, + + pub token_program: Program<'info, Token>, + + pub admin: Signer<'info>, +} + +impl<'info> AdminPerpWithdrawFees<'info> { + pub fn transfer_ctx(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> { + let program = self.token_program.to_account_info(); + let accounts = token::Transfer { + from: self.vault.to_account_info(), + to: self.token_account.to_account_info(), + authority: self.group.to_account_info(), + }; + CpiContext::new(program, accounts) + } +} diff --git a/programs/mango-v4/src/accounts_ix/admin_token_withdraw_fees.rs b/programs/mango-v4/src/accounts_ix/admin_token_withdraw_fees.rs new file mode 100644 index 000000000..50ea5b76c --- /dev/null +++ b/programs/mango-v4/src/accounts_ix/admin_token_withdraw_fees.rs @@ -0,0 +1,45 @@ +use anchor_lang::prelude::*; +use anchor_spl::token; +use anchor_spl::token::Token; +use anchor_spl::token::TokenAccount; + +use crate::error::*; +use crate::state::*; + +#[derive(Accounts)] +pub struct AdminTokenWithdrawFees<'info> { + #[account( + constraint = group.load()?.is_ix_enabled(IxGate::AdminTokenWithdrawFees) @ MangoError::IxIsDisabled, + has_one = admin, + )] + pub group: AccountLoader<'info, Group>, + + #[account( + mut, + has_one = group, + has_one = vault, + )] + pub bank: AccountLoader<'info, Bank>, + + #[account(mut)] + pub vault: Account<'info, TokenAccount>, + + #[account(mut)] + pub token_account: Box>, + + pub token_program: Program<'info, Token>, + + pub admin: Signer<'info>, +} + +impl<'info> AdminTokenWithdrawFees<'info> { + pub fn transfer_ctx(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> { + let program = self.token_program.to_account_info(); + let accounts = token::Transfer { + from: self.vault.to_account_info(), + to: self.token_account.to_account_info(), + authority: self.group.to_account_info(), + }; + CpiContext::new(program, accounts) + } +} diff --git a/programs/mango-v4/src/accounts_ix/mod.rs b/programs/mango-v4/src/accounts_ix/mod.rs index a3d99974c..c864ce22c 100644 --- a/programs/mango-v4/src/accounts_ix/mod.rs +++ b/programs/mango-v4/src/accounts_ix/mod.rs @@ -5,6 +5,8 @@ pub use account_create::*; pub use account_edit::*; pub use account_expand::*; pub use account_toggle_freeze::*; +pub use admin_perp_withdraw_fees::*; +pub use admin_token_withdraw_fees::*; pub use alt_extend::*; pub use alt_set::*; pub use benchmark::*; @@ -77,6 +79,8 @@ mod account_create; mod account_edit; mod account_expand; mod account_toggle_freeze; +mod admin_perp_withdraw_fees; +mod admin_token_withdraw_fees; mod alt_extend; mod alt_set; mod benchmark; diff --git a/programs/mango-v4/src/instructions/admin_perp_withdraw_fees.rs b/programs/mango-v4/src/instructions/admin_perp_withdraw_fees.rs new file mode 100644 index 000000000..0c43929a9 --- /dev/null +++ b/programs/mango-v4/src/instructions/admin_perp_withdraw_fees.rs @@ -0,0 +1,21 @@ +use anchor_lang::prelude::*; +use anchor_spl::token; + +use crate::{accounts_ix::*, group_seeds}; + +pub fn admin_perp_withdraw_fees(ctx: Context) -> Result<()> { + let group = ctx.accounts.group.load()?; + let mut perp_market = ctx.accounts.perp_market.load_mut()?; + + let group_seeds = group_seeds!(group); + let fees = perp_market.fees_settled.floor().to_num::() - perp_market.fees_withdrawn; + let amount = fees.min(ctx.accounts.vault.amount); + token::transfer( + ctx.accounts.transfer_ctx().with_signer(&[group_seeds]), + amount, + )?; + + perp_market.fees_withdrawn += amount; + + Ok(()) +} diff --git a/programs/mango-v4/src/instructions/admin_withdraw_token_fees.rs b/programs/mango-v4/src/instructions/admin_withdraw_token_fees.rs new file mode 100644 index 000000000..fb8dbc168 --- /dev/null +++ b/programs/mango-v4/src/instructions/admin_withdraw_token_fees.rs @@ -0,0 +1,21 @@ +use anchor_lang::prelude::*; +use anchor_spl::token; + +use crate::{accounts_ix::*, group_seeds}; + +pub fn admin_withdraw_token_fees(ctx: Context) -> Result<()> { + let group = ctx.accounts.group.load()?; + let mut bank = ctx.accounts.bank.load_mut()?; + + let group_seeds = group_seeds!(group); + let fees = bank.collected_fees_native.floor().to_num::() - bank.fees_withdrawn; + let amount = fees.min(ctx.accounts.vault.amount); + token::transfer( + ctx.accounts.transfer_ctx().with_signer(&[group_seeds]), + amount, + )?; + + bank.fees_withdrawn += amount; + + Ok(()) +} diff --git a/programs/mango-v4/src/instructions/ix_gate_set.rs b/programs/mango-v4/src/instructions/ix_gate_set.rs index e91a56374..7a9ea0bae 100644 --- a/programs/mango-v4/src/instructions/ix_gate_set.rs +++ b/programs/mango-v4/src/instructions/ix_gate_set.rs @@ -70,6 +70,18 @@ pub fn ix_gate_set(ctx: Context, ix_gate: u128) -> Result<()> { log_if_changed(&group, ix_gate, IxGate::TokenConditionalSwapCreate); log_if_changed(&group, ix_gate, IxGate::TokenConditionalSwapTrigger); log_if_changed(&group, ix_gate, IxGate::TokenConditionalSwapCancel); + log_if_changed(&group, ix_gate, IxGate::OpenbookV2CancelOrder); + log_if_changed(&group, ix_gate, IxGate::OpenbookV2CloseOpenOrders); + log_if_changed(&group, ix_gate, IxGate::OpenbookV2CreateOpenOrders); + log_if_changed(&group, ix_gate, IxGate::OpenbookV2DeregisterMarket); + log_if_changed(&group, ix_gate, IxGate::OpenbookV2EditMarket); + log_if_changed(&group, ix_gate, IxGate::OpenbookV2LiqForceCancelOrders); + log_if_changed(&group, ix_gate, IxGate::OpenbookV2PlaceOrder); + log_if_changed(&group, ix_gate, IxGate::OpenbookV2PlaceTakeOrder); + log_if_changed(&group, ix_gate, IxGate::OpenbookV2RegisterMarket); + log_if_changed(&group, ix_gate, IxGate::OpenbookV2SettleFunds); + log_if_changed(&group, ix_gate, IxGate::AdminTokenWithdrawFees); + log_if_changed(&group, ix_gate, IxGate::AdminPerpWithdrawFees); group.ix_gate = ix_gate; diff --git a/programs/mango-v4/src/instructions/mod.rs b/programs/mango-v4/src/instructions/mod.rs index 485dbd9ea..7929c2d4a 100644 --- a/programs/mango-v4/src/instructions/mod.rs +++ b/programs/mango-v4/src/instructions/mod.rs @@ -4,6 +4,8 @@ pub use account_create::*; pub use account_edit::*; pub use account_expand::*; pub use account_toggle_freeze::*; +pub use admin_perp_withdraw_fees::*; +pub use admin_withdraw_token_fees::*; pub use alt_extend::*; pub use alt_set::*; pub use benchmark::*; @@ -66,6 +68,8 @@ mod account_create; mod account_edit; mod account_expand; mod account_toggle_freeze; +mod admin_perp_withdraw_fees; +mod admin_withdraw_token_fees; mod alt_extend; mod alt_set; mod benchmark; diff --git a/programs/mango-v4/src/instructions/token_register_trustless.rs b/programs/mango-v4/src/instructions/token_register_trustless.rs index 977cd1e0f..b25b6a85f 100644 --- a/programs/mango-v4/src/instructions/token_register_trustless.rs +++ b/programs/mango-v4/src/instructions/token_register_trustless.rs @@ -75,7 +75,9 @@ pub fn token_register_trustless( deposit_weight_scale_start_quote: 5_000_000_000.0, // $5k reduce_only: 2, // deposit-only force_close: 0, - reserved: [0; 2118], + padding: [0; 6], + fees_withdrawn: 0, + reserved: [0; 2104], }; require_gt!(bank.max_rate, MINIMUM_MAX_RATE); diff --git a/programs/mango-v4/src/lib.rs b/programs/mango-v4/src/lib.rs index 597487b7d..a47a6aed5 100644 --- a/programs/mango-v4/src/lib.rs +++ b/programs/mango-v4/src/lib.rs @@ -43,6 +43,18 @@ pub mod mango_v4 { use super::*; use error::*; + pub fn admin_withdraw_token_fees(ctx: Context) -> Result<()> { + #[cfg(feature = "enable-gpl")] + instructions::admin_withdraw_token_fees(ctx)?; + Ok(()) + } + + pub fn admin_perp_withdraw_fees(ctx: Context) -> Result<()> { + #[cfg(feature = "enable-gpl")] + instructions::admin_perp_withdraw_fees(ctx)?; + Ok(()) + } + pub fn group_create( ctx: Context, group_num: u32, diff --git a/programs/mango-v4/src/state/bank.rs b/programs/mango-v4/src/state/bank.rs index a70f30fd0..f090a8ae2 100644 --- a/programs/mango-v4/src/state/bank.rs +++ b/programs/mango-v4/src/state/bank.rs @@ -136,8 +136,14 @@ pub struct Bank { pub reduce_only: u8, pub force_close: u8, + pub padding: [u8; 6], + + // Do separate bookkeping for how many tokens were withdrawn + // This ensures that collected_fees_native is strictly increasing for stats gathering purposes + pub fees_withdrawn: u64, + #[derivative(Debug = "ignore")] - pub reserved: [u8; 2118], + pub reserved: [u8; 2104], } const_assert_eq!( size_of::(), @@ -166,7 +172,9 @@ const_assert_eq!( + 8 + 1 + 1 - + 2118 + + 6 + + 8 + + 2104 ); const_assert_eq!(size_of::(), 3064); const_assert_eq!(size_of::() % 8, 0); @@ -240,7 +248,9 @@ impl Bank { deposit_weight_scale_start_quote: f64::MAX, reduce_only: 0, force_close: 0, - reserved: [0; 2118], + padding: [0; 6], + fees_withdrawn: 0, + reserved: [0; 2104], } } diff --git a/programs/mango-v4/src/state/group.rs b/programs/mango-v4/src/state/group.rs index 83c3724f3..f69c63f10 100644 --- a/programs/mango-v4/src/state/group.rs +++ b/programs/mango-v4/src/state/group.rs @@ -211,6 +211,8 @@ pub enum IxGate { OpenbookV2PlaceTakeOrder = 62, OpenbookV2RegisterMarket = 63, OpenbookV2SettleFunds = 64, + AdminTokenWithdrawFees = 65, + AdminPerpWithdrawFees = 66, // NOTE: Adding new variants requires matching changes in ts and the ix_gate_set instruction. } diff --git a/programs/mango-v4/src/state/perp_market.rs b/programs/mango-v4/src/state/perp_market.rs index 0dc72c86a..80b979170 100644 --- a/programs/mango-v4/src/state/perp_market.rs +++ b/programs/mango-v4/src/state/perp_market.rs @@ -124,8 +124,10 @@ pub struct PerpMarket { pub taker_fee: I80F48, /// Fees accrued in native quote currency + /// these are increased when new fees are paid and decreased when perp_settle_fees is called pub fees_accrued: I80F48, /// Fees settled in native quote currency + /// these are increased when perp_settle_fees is called, and never decreased pub fees_settled: I80F48, /// Fee (in quote native) to charge for ioc orders @@ -170,7 +172,11 @@ pub struct PerpMarket { pub positive_pnl_liquidation_fee: I80F48, - pub reserved: [u8; 1888], + // Do separate bookkeping for how many tokens were withdrawn + // This ensures that fees_settled is strictly increasing for stats gathering purposes + pub fees_withdrawn: u64, + + pub reserved: [u8; 1880], } const_assert_eq!( @@ -206,7 +212,8 @@ const_assert_eq!( + 1 + 7 + 3 * 16 - + 1888 + + 8 + + 1880 ); const_assert_eq!(size_of::(), 2808); const_assert_eq!(size_of::() % 8, 0); @@ -495,7 +502,8 @@ impl PerpMarket { maint_overall_asset_weight: I80F48::ONE, init_overall_asset_weight: I80F48::ONE, positive_pnl_liquidation_fee: I80F48::ZERO, - reserved: [0; 1888], + fees_withdrawn: 0, + reserved: [0; 1880], } } } diff --git a/ts/client/src/clientIxParamBuilder.ts b/ts/client/src/clientIxParamBuilder.ts index 35afe5dcb..f74a8f379 100644 --- a/ts/client/src/clientIxParamBuilder.ts +++ b/ts/client/src/clientIxParamBuilder.ts @@ -182,6 +182,18 @@ export interface IxGateParams { TokenConditionalSwapCreate: boolean; TokenConditionalSwapTrigger: boolean; TokenConditionalSwapCancel: boolean; + OpenbookV2CancelOrder: boolean; + OpenbookV2CloseOpenOrders: boolean; + OpenbookV2CreateOpenOrders: boolean; + OpenbookV2DeregisterMarket: boolean; + OpenbookV2EditMarket: boolean; + OpenbookV2LiqForceCancelOrders: boolean; + OpenbookV2PlaceOrder: boolean; + OpenbookV2PlaceTakeOrder: boolean; + OpenbookV2RegisterMarket: boolean; + OpenbookV2SettleFunds: boolean; + AdminTokenWithdrawFees: boolean; + AdminPerpWithdrawFees: boolean; } // Default with all ixs enabled, use with buildIxGate @@ -244,6 +256,18 @@ export const TrueIxGateParams: IxGateParams = { TokenConditionalSwapCreate: true, TokenConditionalSwapTrigger: true, TokenConditionalSwapCancel: true, + OpenbookV2CancelOrder: true, + OpenbookV2CloseOpenOrders: true, + OpenbookV2CreateOpenOrders: true, + OpenbookV2DeregisterMarket: true, + OpenbookV2EditMarket: true, + OpenbookV2LiqForceCancelOrders: true, + OpenbookV2PlaceOrder: true, + OpenbookV2PlaceTakeOrder: true, + OpenbookV2RegisterMarket: true, + OpenbookV2SettleFunds: true, + AdminTokenWithdrawFees: true, + AdminPerpWithdrawFees: true, }; // build ix gate e.g. buildIxGate(Builder(TrueIxGateParams).TokenDeposit(false).build()).toNumber(), @@ -316,6 +340,18 @@ export function buildIxGate(p: IxGateParams): BN { toggleIx(ixGate, p, 'TokenConditionalSwapCreate', 52); toggleIx(ixGate, p, 'TokenConditionalSwapTrigger', 53); toggleIx(ixGate, p, 'TokenConditionalSwapCancel', 54); + toggleIx(ixGate, p, 'OpenbookV2CancelOrder', 55); + toggleIx(ixGate, p, 'OpenbookV2CloseOpenOrders', 56); + toggleIx(ixGate, p, 'OpenbookV2CreateOpenOrders', 57); + toggleIx(ixGate, p, 'OpenbookV2DeregisterMarket', 58); + toggleIx(ixGate, p, 'OpenbookV2EditMarket', 59); + toggleIx(ixGate, p, 'OpenbookV2LiqForceCancelOrders', 60); + toggleIx(ixGate, p, 'OpenbookV2PlaceOrder', 61); + toggleIx(ixGate, p, 'OpenbookV2PlaceTakeOrder', 62); + toggleIx(ixGate, p, 'OpenbookV2RegisterMarket', 63); + toggleIx(ixGate, p, 'OpenbookV2SettleFunds', 63); + toggleIx(ixGate, p, 'AdminTokenWithdrawFees', 65); + toggleIx(ixGate, p, 'AdminPerpWithdrawFees', 66); return ixGate; }