buyback perp fees with mngo at a discount (#464)
buyback perp fees with mngo Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
5c7a2e3e10
commit
d88d44b34a
|
@ -0,0 +1,52 @@
|
|||
use crate::error::*;
|
||||
use crate::state::*;
|
||||
use anchor_lang::prelude::*;
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct AccountBuybackFeesWithMngo<'info> {
|
||||
#[account(
|
||||
constraint = group.load()?.is_ix_enabled(IxGate::AccountBuybackFeesWithMngo) @ MangoError::IxIsDisabled,
|
||||
constraint = group.load()?.buyback_fees() @ MangoError::SomeError
|
||||
)]
|
||||
pub group: AccountLoader<'info, Group>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
constraint = account.load()?.is_operational() @ MangoError::AccountIsFrozen
|
||||
// owner is checked at #1
|
||||
)]
|
||||
pub account: AccountLoader<'info, MangoAccountFixed>,
|
||||
pub owner: Signer<'info>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
constraint = account.load()?.is_operational() @ MangoError::AccountIsFrozen,
|
||||
address = group.load()?.buyback_fees_swap_mango_account
|
||||
)]
|
||||
pub dao_account: AccountLoader<'info, MangoAccountFixed>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
constraint = mngo_bank.load()?.token_index == group.load()?.mngo_token_index,
|
||||
constraint = mngo_bank.load()?.token_index != 0, // should not be unset
|
||||
)]
|
||||
pub mngo_bank: AccountLoader<'info, Bank>,
|
||||
|
||||
/// CHECK: Oracle can have different account types
|
||||
#[account(address = mngo_bank.load()?.oracle)]
|
||||
pub mngo_oracle: UncheckedAccount<'info>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
constraint = fees_bank.load()?.token_index == QUOTE_TOKEN_INDEX
|
||||
)]
|
||||
pub fees_bank: AccountLoader<'info, Bank>,
|
||||
|
||||
/// CHECK: Oracle can have different account types
|
||||
#[account(address = fees_bank.load()?.oracle)]
|
||||
pub fees_oracle: UncheckedAccount<'info>,
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub use account_buyback_fees_with_mngo::*;
|
||||
pub use account_close::*;
|
||||
pub use account_create::*;
|
||||
pub use account_edit::*;
|
||||
|
@ -53,6 +54,7 @@ pub use token_register_trustless::*;
|
|||
pub use token_update_index_and_rate::*;
|
||||
pub use token_withdraw::*;
|
||||
|
||||
mod account_buyback_fees_with_mngo;
|
||||
mod account_close;
|
||||
mod account_create;
|
||||
mod account_edit;
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
use anchor_lang::prelude::*;
|
||||
use fixed::types::I80F48;
|
||||
|
||||
use crate::accounts_zerocopy::*;
|
||||
use crate::error::MangoError;
|
||||
use crate::state::*;
|
||||
|
||||
use crate::accounts_ix::*;
|
||||
|
||||
pub fn account_buyback_fees_with_mngo(
|
||||
ctx: Context<AccountBuybackFeesWithMngo>,
|
||||
max_buyback: u64,
|
||||
) -> Result<()> {
|
||||
// Cannot buyback from yourself
|
||||
require_keys_neq!(
|
||||
ctx.accounts.account.key(),
|
||||
ctx.accounts.dao_account.key(),
|
||||
MangoError::SomeError
|
||||
);
|
||||
|
||||
let mut account = ctx.accounts.account.load_full_mut()?;
|
||||
// account constraint #1
|
||||
require!(
|
||||
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
|
||||
MangoError::SomeError
|
||||
);
|
||||
|
||||
let mut dao_account = ctx.accounts.dao_account.load_full_mut()?;
|
||||
|
||||
let group = ctx.accounts.group.load()?;
|
||||
|
||||
let mut mngo_bank = ctx.accounts.mngo_bank.load_mut()?;
|
||||
let mut fees_bank = ctx.accounts.fees_bank.load_mut()?;
|
||||
|
||||
let bonus_factor = I80F48::from_num(group.buyback_fees_mngo_bonus_factor);
|
||||
let now_ts = Clock::get()?.unix_timestamp.try_into().unwrap();
|
||||
|
||||
// quick return if nothing to buyback
|
||||
let mut max_buyback = {
|
||||
let dao_fees_token_position = dao_account.ensure_token_position(fees_bank.token_index)?.0;
|
||||
let dao_fees_native = dao_fees_token_position.native(&fees_bank);
|
||||
I80F48::from_num::<u64>(max_buyback.min(account.fixed.buyback_fees_accrued))
|
||||
.min(dao_fees_native)
|
||||
};
|
||||
if max_buyback <= I80F48::ZERO {
|
||||
msg!(
|
||||
"nothing to buyback, (buyback_fees_accrued {})",
|
||||
account.fixed.buyback_fees_accrued
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// if mngo token position has borrows, skip buyback
|
||||
let account_mngo_native = account
|
||||
.token_position(mngo_bank.token_index)
|
||||
.map(|tp| tp.native(&mngo_bank))
|
||||
.unwrap_or(I80F48::ZERO);
|
||||
if account_mngo_native <= I80F48::ZERO {
|
||||
msg!(
|
||||
"account mngo token position ({} native mngo) is <= 0, nothing will be bought back",
|
||||
account_mngo_native
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
let (account_mngo_token_position, account_mngo_raw_token_index, _) =
|
||||
account.ensure_token_position(mngo_bank.token_index)?;
|
||||
|
||||
// compute max mngo to swap for fees
|
||||
let mngo_oracle_price = mngo_bank.oracle_price(
|
||||
&AccountInfoRef::borrow(&ctx.accounts.mngo_oracle.as_ref())?,
|
||||
Some(Clock::get()?.slot),
|
||||
)?;
|
||||
let mngo_buyback_price = mngo_oracle_price * bonus_factor;
|
||||
// mngo is exchanged at a discount
|
||||
let mut max_buyback_mngo = max_buyback / mngo_buyback_price;
|
||||
// buyback is restricted to account's token position
|
||||
max_buyback_mngo = max_buyback_mngo.min(account_mngo_native);
|
||||
max_buyback = max_buyback_mngo * mngo_buyback_price;
|
||||
|
||||
// move mngo from user to dao
|
||||
let (dao_mngo_token_position, dao_mngo_raw_token_index, _) =
|
||||
dao_account.ensure_token_position(mngo_bank.token_index)?;
|
||||
require!(
|
||||
dao_mngo_token_position.indexed_position >= I80F48::ZERO,
|
||||
MangoError::SomeError
|
||||
);
|
||||
let in_use = mngo_bank.withdraw_without_fee(
|
||||
account_mngo_token_position,
|
||||
max_buyback_mngo,
|
||||
now_ts,
|
||||
mngo_oracle_price,
|
||||
)?;
|
||||
if !in_use {
|
||||
account.deactivate_token_position_and_log(
|
||||
account_mngo_raw_token_index,
|
||||
ctx.accounts.account.key(),
|
||||
);
|
||||
}
|
||||
mngo_bank.deposit(dao_mngo_token_position, max_buyback_mngo, now_ts)?;
|
||||
|
||||
// move fees from dao to user
|
||||
let (account_fees_token_position, account_fees_raw_token_index, _) =
|
||||
account.ensure_token_position(fees_bank.token_index)?;
|
||||
let (dao_fees_token_position, dao_fees_raw_token_index, _) =
|
||||
dao_account.ensure_token_position(fees_bank.token_index)?;
|
||||
let dao_fees_native = dao_fees_token_position.native(&fees_bank);
|
||||
assert!(dao_fees_native >= max_buyback);
|
||||
let in_use = fees_bank.withdraw_without_fee(
|
||||
dao_fees_token_position,
|
||||
max_buyback,
|
||||
now_ts,
|
||||
mngo_oracle_price,
|
||||
)?;
|
||||
if !in_use {
|
||||
dao_account.deactivate_token_position_and_log(
|
||||
dao_fees_raw_token_index,
|
||||
ctx.accounts.dao_account.key(),
|
||||
);
|
||||
}
|
||||
let in_use = fees_bank.deposit(account_fees_token_position, max_buyback, now_ts)?;
|
||||
if !in_use {
|
||||
account.deactivate_token_position_and_log(
|
||||
account_fees_raw_token_index,
|
||||
ctx.accounts.account.key(),
|
||||
);
|
||||
}
|
||||
|
||||
account.fixed.buyback_fees_accrued = account
|
||||
.fixed
|
||||
.buyback_fees_accrued
|
||||
.saturating_sub(max_buyback.ceil().to_num::<u64>());
|
||||
msg!(
|
||||
"bought back {} native fees with {} native mngo",
|
||||
max_buyback,
|
||||
max_buyback_mngo
|
||||
);
|
||||
|
||||
// ensure dao mango account has no liabilities after we do the token swap
|
||||
for ele in dao_account.all_token_positions() {
|
||||
require!(!ele.indexed_position.is_negative(), MangoError::SomeError);
|
||||
}
|
||||
require_eq!(
|
||||
dao_account.active_perp_positions().count(),
|
||||
0,
|
||||
MangoError::SomeError
|
||||
);
|
||||
require_eq!(
|
||||
dao_account.active_serum3_orders().count(),
|
||||
0,
|
||||
MangoError::SomeError
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anchor_lang::prelude::*;
|
||||
use fixed::types::{I80F48, U80F48};
|
||||
use solana_program::{log::sol_log_compute_units, program_memory::sol_memcmp};
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
use crate::accounts_ix::*;
|
||||
use crate::{accounts_ix::*, state::TokenIndex};
|
||||
|
||||
// use case - transfer group ownership to governance, where
|
||||
// admin and fast_listing_admin are PDAs
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn group_edit(
|
||||
ctx: Context<GroupEdit>,
|
||||
admin_opt: Option<Pubkey>,
|
||||
|
@ -12,6 +13,10 @@ pub fn group_edit(
|
|||
testing_opt: Option<u8>,
|
||||
version_opt: Option<u8>,
|
||||
deposit_limit_quote_opt: Option<u64>,
|
||||
buyback_fees_opt: Option<bool>,
|
||||
buyback_fees_bonus_factor_opt: Option<f32>,
|
||||
buyback_fees_swap_mango_account_opt: Option<Pubkey>,
|
||||
mngo_token_index_opt: Option<TokenIndex>,
|
||||
) -> Result<()> {
|
||||
let mut group = ctx.accounts.group.load_mut()?;
|
||||
|
||||
|
@ -58,5 +63,38 @@ pub fn group_edit(
|
|||
group.deposit_limit_quote = deposit_limit_quote;
|
||||
}
|
||||
|
||||
if let Some(buyback_fees) = buyback_fees_opt {
|
||||
msg!(
|
||||
"Buyback fees old {:?}, new {:?}",
|
||||
group.buyback_fees,
|
||||
buyback_fees
|
||||
);
|
||||
group.buyback_fees = u8::from(buyback_fees);
|
||||
}
|
||||
if let Some(buyback_fees_mngo_bonus_factor) = buyback_fees_bonus_factor_opt {
|
||||
msg!(
|
||||
"Buyback fees mngo bonus factor old {:?}, new {:?}",
|
||||
group.buyback_fees_mngo_bonus_factor,
|
||||
buyback_fees_mngo_bonus_factor
|
||||
);
|
||||
group.buyback_fees_mngo_bonus_factor = buyback_fees_mngo_bonus_factor;
|
||||
}
|
||||
if let Some(buyback_fees_swap_mango_account) = buyback_fees_swap_mango_account_opt {
|
||||
msg!(
|
||||
"Buyback fees swap mango account old {:?}, new {:?}",
|
||||
group.buyback_fees_swap_mango_account,
|
||||
buyback_fees_swap_mango_account
|
||||
);
|
||||
group.buyback_fees_swap_mango_account = buyback_fees_swap_mango_account;
|
||||
}
|
||||
if let Some(mngo_token_index) = mngo_token_index_opt {
|
||||
msg!(
|
||||
"Mngo token index old {:?}, new {:?}",
|
||||
group.mngo_token_index,
|
||||
mngo_token_index
|
||||
);
|
||||
group.mngo_token_index = mngo_token_index;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub use account_buyback_fees_with_mngo::*;
|
||||
pub use account_close::*;
|
||||
pub use account_create::*;
|
||||
pub use account_edit::*;
|
||||
|
@ -53,6 +54,7 @@ pub use token_register_trustless::*;
|
|||
pub use token_update_index_and_rate::*;
|
||||
pub use token_withdraw::*;
|
||||
|
||||
mod account_buyback_fees_with_mngo;
|
||||
mod account_close;
|
||||
mod account_create;
|
||||
mod account_edit;
|
||||
|
|
|
@ -51,6 +51,7 @@ pub mod mango_v4 {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn group_edit(
|
||||
ctx: Context<GroupEdit>,
|
||||
admin_opt: Option<Pubkey>,
|
||||
|
@ -59,6 +60,10 @@ pub mod mango_v4 {
|
|||
testing_opt: Option<u8>,
|
||||
version_opt: Option<u8>,
|
||||
deposit_limit_quote_opt: Option<u64>,
|
||||
buyback_fees_opt: Option<bool>,
|
||||
buyback_fees_bonus_factor_opt: Option<f32>,
|
||||
buyback_fees_swap_mango_account_opt: Option<Pubkey>,
|
||||
mngo_token_index_opt: Option<TokenIndex>,
|
||||
) -> Result<()> {
|
||||
#[cfg(feature = "enable-gpl")]
|
||||
instructions::group_edit(
|
||||
|
@ -69,6 +74,10 @@ pub mod mango_v4 {
|
|||
testing_opt,
|
||||
version_opt,
|
||||
deposit_limit_quote_opt,
|
||||
buyback_fees_opt,
|
||||
buyback_fees_bonus_factor_opt,
|
||||
buyback_fees_swap_mango_account_opt,
|
||||
mngo_token_index_opt,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -270,6 +279,15 @@ pub mod mango_v4 {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn account_buyback_fees_with_mngo(
|
||||
ctx: Context<AccountBuybackFeesWithMngo>,
|
||||
max_buyback: u64,
|
||||
) -> Result<()> {
|
||||
#[cfg(feature = "enable-gpl")]
|
||||
instructions::account_buyback_fees_with_mngo(ctx, max_buyback)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// todo:
|
||||
// ckamm: generally, using an I80F48 arg will make it harder to call
|
||||
// because generic anchor clients won't know how to deal with it
|
||||
|
|
|
@ -20,7 +20,9 @@ pub struct Group {
|
|||
// TODO: unused, use case - listing shit tokens with conservative parameters (mostly defaults)
|
||||
pub fast_listing_admin: Pubkey,
|
||||
|
||||
pub padding: [u8; 4],
|
||||
// This is the token index of the mngo token listed on the group
|
||||
pub mngo_token_index: TokenIndex,
|
||||
pub padding: [u8; 2],
|
||||
|
||||
pub insurance_vault: Pubkey,
|
||||
pub insurance_mint: Pubkey,
|
||||
|
@ -31,7 +33,11 @@ pub struct Group {
|
|||
|
||||
pub version: u8,
|
||||
|
||||
pub padding2: [u8; 5],
|
||||
// Buyback fees with Mngo: allow exchanging fees with mngo at a bonus
|
||||
pub buyback_fees: u8,
|
||||
// Buyback fees with Mngo: how much should the bonus be,
|
||||
// e.g. a bonus factor of 1.2 means 120$ worth fees could be swapped for mngo worth 100$ at current market price
|
||||
pub buyback_fees_mngo_bonus_factor: f32,
|
||||
|
||||
pub address_lookup_tables: [Pubkey; 20],
|
||||
|
||||
|
@ -45,16 +51,29 @@ pub struct Group {
|
|||
// 0 is chosen as enabled, becase we want to start out with all ixs enabled, 1 is disabled
|
||||
pub ix_gate: u128,
|
||||
|
||||
pub reserved: [u8; 1864],
|
||||
// Buyback fees with Mngo:
|
||||
// A mango account which would be counter party for settling fees with mngo
|
||||
// This ensures that the system doesn't have a net deficit of tokens
|
||||
// The workflow should be something like this
|
||||
// - the dao deposits quote tokens in its respective mango account
|
||||
// - the user deposits some mngo tokens in his mango account
|
||||
// - the user then claims quote for mngo at a bonus rate
|
||||
pub buyback_fees_swap_mango_account: Pubkey,
|
||||
|
||||
pub reserved: [u8; 1832],
|
||||
}
|
||||
const_assert_eq!(
|
||||
size_of::<Group>(),
|
||||
32 + 4 + 32 * 2 + 4 + 32 * 2 + 3 + 5 + 20 * 32 + 32 + 8 + 16 + 1864
|
||||
32 + 4 + 32 * 2 + 4 + 32 * 2 + 4 + 4 + 20 * 32 + 32 + 8 + 16 + 32 + 1832
|
||||
);
|
||||
const_assert_eq!(size_of::<Group>(), 2736);
|
||||
const_assert_eq!(size_of::<Group>() % 8, 0);
|
||||
|
||||
impl Group {
|
||||
pub fn buyback_fees(&self) -> bool {
|
||||
self.buyback_fees == 1
|
||||
}
|
||||
|
||||
pub fn is_testing(&self) -> bool {
|
||||
self.testing == 1
|
||||
}
|
||||
|
@ -139,6 +158,7 @@ pub enum IxGate {
|
|||
TokenRegisterTrustless = 45,
|
||||
TokenUpdateIndexAndRate = 46,
|
||||
TokenWithdraw = 47,
|
||||
AccountBuybackFeesWithMngo = 48,
|
||||
}
|
||||
|
||||
// note: using creator instead of admin, since admin can be changed
|
||||
|
|
|
@ -87,7 +87,9 @@ pub struct MangoAccount {
|
|||
|
||||
pub frozen_until: u64,
|
||||
|
||||
pub reserved: [u8; 232],
|
||||
pub buyback_fees_accrued: u64,
|
||||
|
||||
pub reserved: [u8; 224],
|
||||
|
||||
// dynamic
|
||||
pub header_version: u8,
|
||||
|
@ -122,7 +124,8 @@ impl MangoAccount {
|
|||
net_deposits: 0,
|
||||
health_region_begin_init_health: 0,
|
||||
frozen_until: 0,
|
||||
reserved: [0; 232],
|
||||
buyback_fees_accrued: 0,
|
||||
reserved: [0; 224],
|
||||
header_version: DEFAULT_MANGO_ACCOUNT_VERSION,
|
||||
padding3: Default::default(),
|
||||
padding4: Default::default(),
|
||||
|
@ -204,9 +207,13 @@ pub struct MangoAccountFixed {
|
|||
pub perp_spot_transfers: i64,
|
||||
pub health_region_begin_init_health: i64,
|
||||
pub frozen_until: u64,
|
||||
pub reserved: [u8; 232],
|
||||
pub buyback_fees_accrued: u64,
|
||||
pub reserved: [u8; 224],
|
||||
}
|
||||
const_assert_eq!(size_of::<MangoAccountFixed>(), 32 * 4 + 8 + 3 * 8 + 8 + 232);
|
||||
const_assert_eq!(
|
||||
size_of::<MangoAccountFixed>(),
|
||||
32 * 4 + 8 + 3 * 8 + 8 + 8 + 224
|
||||
);
|
||||
const_assert_eq!(size_of::<MangoAccountFixed>(), 400);
|
||||
const_assert_eq!(size_of::<MangoAccountFixed>() % 8, 0);
|
||||
|
||||
|
@ -891,13 +898,15 @@ impl<
|
|||
perp_market: &mut PerpMarket,
|
||||
fill: &FillEvent,
|
||||
) -> Result<()> {
|
||||
let pa = self.perp_position_mut(perp_market_index)?;
|
||||
pa.settle_funding(perp_market);
|
||||
|
||||
let side = fill.taker_side().invert_side();
|
||||
let (base_change, quote_change) = fill.base_quote_change(side);
|
||||
let quote = I80F48::from(perp_market.quote_lot_size) * I80F48::from(quote_change);
|
||||
let fees = quote.abs() * I80F48::from_num(fill.maker_fee);
|
||||
if fees.is_positive() {
|
||||
self.fixed_mut().buyback_fees_accrued += fees.floor().to_num::<u64>();
|
||||
}
|
||||
let pa = self.perp_position_mut(perp_market_index)?;
|
||||
pa.settle_funding(perp_market);
|
||||
pa.record_trading_fee(fees);
|
||||
pa.record_trade(perp_market, base_change, quote);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::state::{MangoAccountRefMut, PerpPosition};
|
||||
use crate::state::MangoAccountRefMut;
|
||||
use crate::{
|
||||
error::*,
|
||||
state::{orderbook::bookside::*, EventQueue, PerpMarket},
|
||||
|
@ -58,16 +58,17 @@ impl<'a> Orderbook<'a> {
|
|||
let post_only = order.is_post_only();
|
||||
let mut post_target = order.post_target();
|
||||
let (price_lots, price_data) = order.price(now_ts, oracle_price_lots, self)?;
|
||||
let perp_position = mango_account.perp_position_mut(market.perp_market_index)?;
|
||||
|
||||
// generate new order id
|
||||
let order_id = market.gen_order_id(side, price_data);
|
||||
|
||||
// IOC orders have a fee penalty applied regardless of match
|
||||
if order.needs_penalty_fee() {
|
||||
apply_penalty(market, perp_position)?;
|
||||
apply_penalty(market, mango_account)?;
|
||||
}
|
||||
|
||||
let perp_position = mango_account.perp_position_mut(market.perp_market_index)?;
|
||||
|
||||
// Iterate through book and match against this new order.
|
||||
//
|
||||
// Any changes to matching orders on the other side of the book are collected in
|
||||
|
@ -164,7 +165,7 @@ impl<'a> Orderbook<'a> {
|
|||
// realized when the fill event gets executed
|
||||
if total_quote_lots_taken > 0 || total_base_lots_taken > 0 {
|
||||
perp_position.add_taker_trade(side, total_base_lots_taken, total_quote_lots_taken);
|
||||
apply_fees(market, perp_position, total_quote_lots_taken)?;
|
||||
apply_fees(market, mango_account, total_quote_lots_taken)?;
|
||||
}
|
||||
|
||||
// Apply changes to matched asks (handles invalidate on delete!)
|
||||
|
@ -350,7 +351,7 @@ impl<'a> Orderbook<'a> {
|
|||
/// both the maker and taker fees.
|
||||
fn apply_fees(
|
||||
market: &mut PerpMarket,
|
||||
perp_position: &mut PerpPosition,
|
||||
account: &mut MangoAccountRefMut,
|
||||
quote_lots: i64,
|
||||
) -> Result<()> {
|
||||
let quote_native = I80F48::from_num(market.quote_lot_size * quote_lots);
|
||||
|
@ -362,6 +363,8 @@ fn apply_fees(
|
|||
require_gte!(taker_fees, 0);
|
||||
|
||||
// The maker fees apply to the maker's account only when the fill event is consumed.
|
||||
account.fixed.buyback_fees_accrued += taker_fees.floor().to_num::<u64>();
|
||||
let perp_position = account.perp_position_mut(market.perp_market_index)?;
|
||||
perp_position.record_trading_fee(taker_fees);
|
||||
perp_position.taker_volume += taker_fees.to_num::<u64>();
|
||||
|
||||
|
@ -374,8 +377,11 @@ fn apply_fees(
|
|||
}
|
||||
|
||||
/// Applies a fixed penalty fee to the account, and update the market's fees_accrued
|
||||
fn apply_penalty(market: &mut PerpMarket, perp_position: &mut PerpPosition) -> Result<()> {
|
||||
fn apply_penalty(market: &mut PerpMarket, account: &mut MangoAccountRefMut) -> Result<()> {
|
||||
let fee_penalty = I80F48::from_num(market.fee_penalty);
|
||||
account.fixed.buyback_fees_accrued += fee_penalty.floor().to_num::<u64>();
|
||||
|
||||
let perp_position = account.perp_position_mut(market.perp_market_index)?;
|
||||
perp_position.record_trading_fee(fee_penalty);
|
||||
market.fees_accrued += fee_penalty;
|
||||
Ok(())
|
||||
|
|
|
@ -17,6 +17,7 @@ mod test_basic;
|
|||
mod test_benchmark;
|
||||
mod test_borrow_limits;
|
||||
mod test_delegate;
|
||||
mod test_fees_buyback_with_mngo;
|
||||
mod test_health_compute;
|
||||
mod test_health_region;
|
||||
mod test_ix_gate_set;
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_fees_buyback_with_mngo() -> Result<(), TransportError> {
|
||||
let context = TestContext::new().await;
|
||||
let solana = &context.solana.clone();
|
||||
|
||||
let admin = TestKeypair::new();
|
||||
let owner = context.users[0].key;
|
||||
let payer = context.users[1].key;
|
||||
let mints = &context.mints[0..2];
|
||||
|
||||
//
|
||||
// SETUP: Create a group and an account
|
||||
//
|
||||
|
||||
let GroupWithTokens { group, tokens, .. } = GroupWithTokensConfig {
|
||||
admin,
|
||||
payer,
|
||||
mints: mints.to_vec(),
|
||||
..GroupWithTokensConfig::default()
|
||||
}
|
||||
.create(solana)
|
||||
.await;
|
||||
|
||||
let deposit_amount = 100_000_000;
|
||||
let account_0 = create_funded_account(
|
||||
solana,
|
||||
group,
|
||||
owner,
|
||||
0,
|
||||
&context.users[1],
|
||||
mints,
|
||||
deposit_amount,
|
||||
0,
|
||||
)
|
||||
.await;
|
||||
let account_1 = create_funded_account(
|
||||
solana,
|
||||
group,
|
||||
owner,
|
||||
1,
|
||||
&context.users[1],
|
||||
mints,
|
||||
deposit_amount,
|
||||
0,
|
||||
)
|
||||
.await;
|
||||
let account_2 = create_funded_account(
|
||||
solana,
|
||||
group,
|
||||
owner,
|
||||
2,
|
||||
&context.users[1],
|
||||
mints,
|
||||
deposit_amount,
|
||||
0,
|
||||
)
|
||||
.await;
|
||||
|
||||
//
|
||||
// Create a perp market
|
||||
//
|
||||
let mango_v4::accounts::PerpCreateMarket { perp_market, .. } = send_tx(
|
||||
solana,
|
||||
PerpCreateMarketInstruction {
|
||||
group,
|
||||
admin,
|
||||
payer,
|
||||
perp_market_index: 0,
|
||||
quote_lot_size: 10,
|
||||
base_lot_size: 100,
|
||||
maint_base_asset_weight: 0.975,
|
||||
init_base_asset_weight: 0.95,
|
||||
maint_base_liab_weight: 1.025,
|
||||
init_base_liab_weight: 1.05,
|
||||
base_liquidation_fee: 0.012,
|
||||
maker_fee: -0.01,
|
||||
taker_fee: 0.02,
|
||||
settle_pnl_limit_factor: -1.0,
|
||||
settle_pnl_limit_window_size_ts: 24 * 60 * 60,
|
||||
..PerpCreateMarketInstruction::with_new_book_and_queue(solana, &tokens[0]).await
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let price_lots = {
|
||||
let perp_market = solana.get_account::<PerpMarket>(perp_market).await;
|
||||
perp_market.native_price_to_lot(I80F48::from(1))
|
||||
};
|
||||
|
||||
//
|
||||
// Place a bid, corresponding ask, and consume event
|
||||
//
|
||||
send_tx(
|
||||
solana,
|
||||
PerpPlaceOrderInstruction {
|
||||
account: account_0,
|
||||
perp_market,
|
||||
owner,
|
||||
side: Side::Bid,
|
||||
price_lots,
|
||||
max_base_lots: 10,
|
||||
max_quote_lots: i64::MAX,
|
||||
reduce_only: false,
|
||||
client_order_id: 5,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
send_tx(
|
||||
solana,
|
||||
PerpPlaceOrderInstruction {
|
||||
account: account_1,
|
||||
perp_market,
|
||||
owner,
|
||||
side: Side::Ask,
|
||||
price_lots,
|
||||
max_base_lots: 10,
|
||||
max_quote_lots: i64::MAX,
|
||||
reduce_only: false,
|
||||
client_order_id: 6,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
send_tx(
|
||||
solana,
|
||||
PerpConsumeEventsInstruction {
|
||||
perp_market,
|
||||
mango_accounts: vec![account_0, account_1],
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
//
|
||||
// Test: Account buyback fees accrued with mngo
|
||||
//
|
||||
send_tx(
|
||||
solana,
|
||||
GroupEditFeeParameters {
|
||||
group,
|
||||
admin,
|
||||
fees_mngo_token_index: 1 as TokenIndex,
|
||||
fees_swap_mango_account: account_2,
|
||||
fees_mngo_bonus_factor: 1.2,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
||||
let before_fees_accrued = mango_account_1.buyback_fees_accrued;
|
||||
let fees_token_position_before =
|
||||
mango_account_1.tokens[0].native(&solana.get_account::<Bank>(tokens[0].bank).await);
|
||||
let mngo_token_position_before =
|
||||
mango_account_1.tokens[1].native(&solana.get_account::<Bank>(tokens[1].bank).await);
|
||||
send_tx(
|
||||
solana,
|
||||
AccountBuybackFeesWithMngo {
|
||||
owner,
|
||||
account: account_1,
|
||||
mngo_bank: tokens[1].bank,
|
||||
fees_bank: tokens[0].bank,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let mango_account_1 = solana.get_account::<MangoAccount>(account_1).await;
|
||||
let after_fees_accrued = solana
|
||||
.get_account::<MangoAccount>(account_1)
|
||||
.await
|
||||
.buyback_fees_accrued;
|
||||
let fees_token_position_after =
|
||||
mango_account_1.tokens[0].native(&solana.get_account::<Bank>(tokens[0].bank).await);
|
||||
let mngo_token_position_after =
|
||||
mango_account_1.tokens[1].native(&solana.get_account::<Bank>(tokens[1].bank).await);
|
||||
|
||||
assert_eq!(before_fees_accrued - after_fees_accrued, 19);
|
||||
|
||||
// token[1] swapped at discount for token[0]
|
||||
assert!(
|
||||
(fees_token_position_after - fees_token_position_before) - I80F48::from_num(20)
|
||||
< I80F48::from_num(0.000001)
|
||||
);
|
||||
assert!(
|
||||
(mngo_token_position_before - mngo_token_position_after) - I80F48::from_num(16.666666)
|
||||
< I80F48::from_num(0.000001)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1503,6 +1503,59 @@ impl ClientInstruction for GroupCreateInstruction {
|
|||
}
|
||||
}
|
||||
|
||||
fn group_edit_instruction_default() -> mango_v4::instruction::GroupEdit {
|
||||
mango_v4::instruction::GroupEdit {
|
||||
admin_opt: None,
|
||||
fast_listing_admin_opt: None,
|
||||
security_admin_opt: None,
|
||||
testing_opt: None,
|
||||
version_opt: None,
|
||||
deposit_limit_quote_opt: None,
|
||||
buyback_fees_opt: None,
|
||||
buyback_fees_bonus_factor_opt: None,
|
||||
buyback_fees_swap_mango_account_opt: None,
|
||||
mngo_token_index_opt: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GroupEditFeeParameters {
|
||||
pub group: Pubkey,
|
||||
pub admin: TestKeypair,
|
||||
pub fees_mngo_bonus_factor: f32,
|
||||
pub fees_mngo_token_index: TokenIndex,
|
||||
pub fees_swap_mango_account: Pubkey,
|
||||
}
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ClientInstruction for GroupEditFeeParameters {
|
||||
type Accounts = mango_v4::accounts::GroupEdit;
|
||||
type Instruction = mango_v4::instruction::GroupEdit;
|
||||
async fn to_instruction(
|
||||
&self,
|
||||
_account_loader: impl ClientAccountLoader + 'async_trait,
|
||||
) -> (Self::Accounts, instruction::Instruction) {
|
||||
let program_id = mango_v4::id();
|
||||
let instruction = Self::Instruction {
|
||||
buyback_fees_opt: Some(true),
|
||||
buyback_fees_bonus_factor_opt: Some(self.fees_mngo_bonus_factor),
|
||||
buyback_fees_swap_mango_account_opt: Some(self.fees_swap_mango_account),
|
||||
mngo_token_index_opt: Some(self.fees_mngo_token_index),
|
||||
..group_edit_instruction_default()
|
||||
};
|
||||
|
||||
let accounts = Self::Accounts {
|
||||
group: self.group,
|
||||
admin: self.admin.pubkey(),
|
||||
};
|
||||
|
||||
let instruction = make_instruction(program_id, &accounts, instruction);
|
||||
(accounts, instruction)
|
||||
}
|
||||
|
||||
fn signers(&self) -> Vec<TestKeypair> {
|
||||
vec![self.admin]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IxGateSetInstruction {
|
||||
pub group: Pubkey,
|
||||
pub admin: TestKeypair,
|
||||
|
@ -1765,6 +1818,55 @@ impl ClientInstruction for AccountCloseInstruction {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct AccountBuybackFeesWithMngo {
|
||||
pub owner: TestKeypair,
|
||||
pub account: Pubkey,
|
||||
pub mngo_bank: Pubkey,
|
||||
pub fees_bank: Pubkey,
|
||||
}
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ClientInstruction for AccountBuybackFeesWithMngo {
|
||||
type Accounts = mango_v4::accounts::AccountBuybackFeesWithMngo;
|
||||
type Instruction = mango_v4::instruction::AccountBuybackFeesWithMngo;
|
||||
async fn to_instruction(
|
||||
&self,
|
||||
account_loader: impl ClientAccountLoader + 'async_trait,
|
||||
) -> (Self::Accounts, instruction::Instruction) {
|
||||
let program_id = mango_v4::id();
|
||||
let instruction = Self::Instruction {
|
||||
max_buyback: u64::MAX,
|
||||
};
|
||||
|
||||
let account = account_loader
|
||||
.load_mango_account(&self.account)
|
||||
.await
|
||||
.unwrap();
|
||||
let group = account_loader
|
||||
.load::<Group>(&account.fixed.group)
|
||||
.await
|
||||
.unwrap();
|
||||
let mngo_bank: Bank = account_loader.load(&self.mngo_bank).await.unwrap();
|
||||
let fees_bank: Bank = account_loader.load(&self.fees_bank).await.unwrap();
|
||||
let accounts = Self::Accounts {
|
||||
group: account.fixed.group,
|
||||
owner: self.owner.pubkey(),
|
||||
account: self.account,
|
||||
dao_account: group.buyback_fees_swap_mango_account,
|
||||
mngo_bank: self.mngo_bank,
|
||||
mngo_oracle: mngo_bank.oracle,
|
||||
fees_bank: self.fees_bank,
|
||||
fees_oracle: fees_bank.oracle,
|
||||
};
|
||||
|
||||
let instruction = make_instruction(program_id, &accounts, instruction);
|
||||
(accounts, instruction)
|
||||
}
|
||||
|
||||
fn signers(&self) -> Vec<TestKeypair> {
|
||||
vec![self.owner]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Serum3RegisterMarketInstruction {
|
||||
pub group: Pubkey,
|
||||
pub admin: TestKeypair,
|
||||
|
|
|
@ -475,7 +475,7 @@ export class MintInfo {
|
|||
obj.vaults,
|
||||
obj.oracle,
|
||||
obj.registrationTime,
|
||||
obj.groupInsuranceFund,
|
||||
obj.groupInsuranceFund == 1,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -488,7 +488,7 @@ export class MintInfo {
|
|||
public vaults: PublicKey[],
|
||||
public oracle: PublicKey,
|
||||
public registrationTime: BN,
|
||||
public groupInsuranceFund: number,
|
||||
public groupInsuranceFund: boolean,
|
||||
) {}
|
||||
|
||||
public firstBank(): PublicKey {
|
||||
|
|
|
@ -32,13 +32,18 @@ export class Group {
|
|||
groupNum: number;
|
||||
admin: PublicKey;
|
||||
fastListingAdmin: PublicKey;
|
||||
securityAdmin: PublicKey;
|
||||
feesMngoTokenIndex: number;
|
||||
insuranceMint: PublicKey;
|
||||
insuranceVault: PublicKey;
|
||||
testing: number;
|
||||
version: number;
|
||||
ixGate: BN;
|
||||
feesPayWithMngo: number;
|
||||
feesMngoBonusFactor: number;
|
||||
addressLookupTables: PublicKey[];
|
||||
securityAdmin: PublicKey;
|
||||
depositLimitQuote: BN;
|
||||
ixGate: BN;
|
||||
feesSwapMangoAccount: PublicKey;
|
||||
},
|
||||
): Group {
|
||||
return new Group(
|
||||
|
@ -47,13 +52,18 @@ export class Group {
|
|||
obj.groupNum,
|
||||
obj.admin,
|
||||
obj.fastListingAdmin,
|
||||
obj.securityAdmin,
|
||||
obj.feesMngoTokenIndex as TokenIndex,
|
||||
obj.insuranceMint,
|
||||
obj.insuranceVault,
|
||||
obj.testing,
|
||||
obj.version,
|
||||
obj.ixGate,
|
||||
obj.feesPayWithMngo == 1,
|
||||
obj.feesMngoBonusFactor,
|
||||
obj.addressLookupTables,
|
||||
obj.securityAdmin,
|
||||
obj.depositLimitQuote,
|
||||
obj.ixGate,
|
||||
obj.feesSwapMangoAccount,
|
||||
[], // addressLookupTablesList
|
||||
new Map(), // banksMapByName
|
||||
new Map(), // banksMapByMint
|
||||
|
@ -76,13 +86,18 @@ export class Group {
|
|||
public groupNum: number,
|
||||
public admin: PublicKey,
|
||||
public fastListingAdmin: PublicKey,
|
||||
public securityAdmin: PublicKey,
|
||||
public feesMngoTokenIndex: TokenIndex,
|
||||
public insuranceMint: PublicKey,
|
||||
public insuranceVault: PublicKey,
|
||||
public testing: number,
|
||||
public version: number,
|
||||
public ixGate: BN,
|
||||
public feesPayWithMngo: boolean,
|
||||
public feesMngoBonusFactor: number,
|
||||
public addressLookupTables: PublicKey[],
|
||||
public securityAdmin: PublicKey,
|
||||
public depositLimitQuote,
|
||||
public ixGate: BN,
|
||||
public feesSwapMangoAccount: PublicKey,
|
||||
public addressLookupTablesList: AddressLookupTableAccount[],
|
||||
public banksMapByName: Map<string, Bank[]>,
|
||||
public banksMapByMint: Map<string, Bank[]>,
|
||||
|
|
|
@ -32,6 +32,7 @@ export class MangoAccount {
|
|||
perpSpotTransfers: BN;
|
||||
healthRegionBeginInitHealth: BN;
|
||||
frozenUntil: BN;
|
||||
buybackFeesAccrued: BN;
|
||||
headerVersion: number;
|
||||
tokens: unknown;
|
||||
serum3: unknown;
|
||||
|
@ -52,6 +53,7 @@ export class MangoAccount {
|
|||
obj.perpSpotTransfers,
|
||||
obj.healthRegionBeginInitHealth,
|
||||
obj.frozenUntil,
|
||||
obj.buybackFeesAccrued,
|
||||
obj.headerVersion,
|
||||
obj.tokens as TokenPositionDto[],
|
||||
obj.serum3 as Serum3PositionDto[],
|
||||
|
@ -74,6 +76,7 @@ export class MangoAccount {
|
|||
public perpSpotTransfers: BN,
|
||||
public healthRegionBeginInitHealth: BN,
|
||||
public frozenUntil: BN,
|
||||
public buybackFeesAccrued: BN,
|
||||
public headerVersion: number,
|
||||
tokens: TokenPositionDto[],
|
||||
serum3: Serum3PositionDto[],
|
||||
|
|
|
@ -158,6 +158,10 @@ export class MangoClient {
|
|||
testing?: number,
|
||||
version?: number,
|
||||
depositLimitQuote?: BN,
|
||||
feesPayWithMngo?: boolean,
|
||||
feesMngoBonusRate?: number,
|
||||
feesSwapMangoAccount?: PublicKey,
|
||||
feesMngoTokenIndex?: TokenIndex,
|
||||
): Promise<TransactionSignature> {
|
||||
const ix = await this.program.methods
|
||||
.groupEdit(
|
||||
|
@ -167,6 +171,10 @@ export class MangoClient {
|
|||
testing ?? null,
|
||||
version ?? null,
|
||||
depositLimitQuote !== undefined ? depositLimitQuote : null,
|
||||
feesPayWithMngo ?? null,
|
||||
feesMngoBonusRate ?? null,
|
||||
feesSwapMangoAccount ?? null,
|
||||
feesMngoTokenIndex ?? null,
|
||||
)
|
||||
.accounts({
|
||||
group: group.publicKey,
|
||||
|
|
|
@ -287,6 +287,8 @@ export function buildIxGate(p: IxGateParams): BN {
|
|||
toggleIx(ixGate, p, 'TokenRegisterTrustless', 45);
|
||||
toggleIx(ixGate, p, 'TokenUpdateIndexAndRate', 46);
|
||||
toggleIx(ixGate, p, 'TokenWithdraw', 47);
|
||||
toggleIx(ixGate, p, 'AccountSettleFeesWithMngo', 48);
|
||||
|
||||
return ixGate;
|
||||
}
|
||||
|
||||
|
|
|
@ -144,6 +144,30 @@ export type MangoV4 = {
|
|||
"type": {
|
||||
"option": "u64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "feesPayWithMngoOpt",
|
||||
"type": {
|
||||
"option": "bool"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "feesMngoBonusFactorOpt",
|
||||
"type": {
|
||||
"option": "f32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "feesSwapMangoAccountOpt",
|
||||
"type": {
|
||||
"option": "publicKey"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "feesMngoTokenIndexOpt",
|
||||
"type": {
|
||||
"option": "u16"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1094,6 +1118,57 @@ export type MangoV4 = {
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "accountBuybackFeesWithMngo",
|
||||
"accounts": [
|
||||
{
|
||||
"name": "group",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "account",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "owner",
|
||||
"isMut": false,
|
||||
"isSigner": true
|
||||
},
|
||||
{
|
||||
"name": "daoAccount",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "mngoBank",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "mngoOracle",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "feesBank",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "feesOracle",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "maxBuyback",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stubOracleCreate",
|
||||
"accounts": [
|
||||
|
@ -3947,12 +4022,16 @@ export type MangoV4 = {
|
|||
"name": "fastListingAdmin",
|
||||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "feesMngoTokenIndex",
|
||||
"type": "u16"
|
||||
},
|
||||
{
|
||||
"name": "padding",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
4
|
||||
2
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -3977,13 +4056,12 @@ export type MangoV4 = {
|
|||
"type": "u8"
|
||||
},
|
||||
{
|
||||
"name": "padding2",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
5
|
||||
]
|
||||
}
|
||||
"name": "feesPayWithMngo",
|
||||
"type": "u8"
|
||||
},
|
||||
{
|
||||
"name": "feesMngoBonusFactor",
|
||||
"type": "f32"
|
||||
},
|
||||
{
|
||||
"name": "addressLookupTables",
|
||||
|
@ -4006,12 +4084,16 @@ export type MangoV4 = {
|
|||
"name": "ixGate",
|
||||
"type": "u128"
|
||||
},
|
||||
{
|
||||
"name": "feesSwapMangoAccount",
|
||||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "reserved",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
1864
|
||||
1832
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -4104,12 +4186,16 @@ export type MangoV4 = {
|
|||
"name": "frozenUntil",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "discountBuybackFeesAccrued",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "reserved",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
232
|
||||
224
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -5679,12 +5765,16 @@ export type MangoV4 = {
|
|||
"name": "frozenUntil",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "discountBuybackFeesAccrued",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "reserved",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
232
|
||||
224
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -6682,6 +6772,9 @@ export type MangoV4 = {
|
|||
},
|
||||
{
|
||||
"name": "TokenWithdraw"
|
||||
},
|
||||
{
|
||||
"name": "AccountBuybackFeesWithMngo"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -8490,6 +8583,30 @@ export const IDL: MangoV4 = {
|
|||
"type": {
|
||||
"option": "u64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "feesPayWithMngoOpt",
|
||||
"type": {
|
||||
"option": "bool"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "feesMngoBonusFactorOpt",
|
||||
"type": {
|
||||
"option": "f32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "feesSwapMangoAccountOpt",
|
||||
"type": {
|
||||
"option": "publicKey"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "feesMngoTokenIndexOpt",
|
||||
"type": {
|
||||
"option": "u16"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -9440,6 +9557,57 @@ export const IDL: MangoV4 = {
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "accountBuybackFeesWithMngo",
|
||||
"accounts": [
|
||||
{
|
||||
"name": "group",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "account",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "owner",
|
||||
"isMut": false,
|
||||
"isSigner": true
|
||||
},
|
||||
{
|
||||
"name": "daoAccount",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "mngoBank",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "mngoOracle",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "feesBank",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "feesOracle",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "maxBuyback",
|
||||
"type": "u64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stubOracleCreate",
|
||||
"accounts": [
|
||||
|
@ -12293,12 +12461,16 @@ export const IDL: MangoV4 = {
|
|||
"name": "fastListingAdmin",
|
||||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "feesMngoTokenIndex",
|
||||
"type": "u16"
|
||||
},
|
||||
{
|
||||
"name": "padding",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
4
|
||||
2
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -12323,13 +12495,12 @@ export const IDL: MangoV4 = {
|
|||
"type": "u8"
|
||||
},
|
||||
{
|
||||
"name": "padding2",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
5
|
||||
]
|
||||
}
|
||||
"name": "feesPayWithMngo",
|
||||
"type": "u8"
|
||||
},
|
||||
{
|
||||
"name": "feesMngoBonusFactor",
|
||||
"type": "f32"
|
||||
},
|
||||
{
|
||||
"name": "addressLookupTables",
|
||||
|
@ -12352,12 +12523,16 @@ export const IDL: MangoV4 = {
|
|||
"name": "ixGate",
|
||||
"type": "u128"
|
||||
},
|
||||
{
|
||||
"name": "feesSwapMangoAccount",
|
||||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "reserved",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
1864
|
||||
1832
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -12450,12 +12625,16 @@ export const IDL: MangoV4 = {
|
|||
"name": "frozenUntil",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "discountBuybackFeesAccrued",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "reserved",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
232
|
||||
224
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -14025,12 +14204,16 @@ export const IDL: MangoV4 = {
|
|||
"name": "frozenUntil",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "discountBuybackFeesAccrued",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "reserved",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
232
|
||||
224
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -15028,6 +15211,9 @@ export const IDL: MangoV4 = {
|
|||
},
|
||||
{
|
||||
"name": "TokenWithdraw"
|
||||
},
|
||||
{
|
||||
"name": "AccountBuybackFeesWithMngo"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue