Add referrer rebates
This commit is contained in:
parent
27d6bdf70b
commit
e38289de2a
|
@ -117,6 +117,11 @@ impl FeeTier {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn referrer_rebate(amount: u64) -> u64 {
|
||||
amount / 5
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -126,12 +131,12 @@ mod tests {
|
|||
#[test]
|
||||
fn positive_net_fees(tt: FeeTier, mt: FeeTier, qty in 1..=std::u64::MAX) {
|
||||
let fee = tt.taker_fee(qty);
|
||||
let rebate = mt.maker_rebate(qty);
|
||||
let rebate = mt.maker_rebate(qty) + referrer_rebate(fee);
|
||||
assert!(fee > rebate);
|
||||
let net_bps_u64f64 = (fee - rebate) as u128 * 10_000;
|
||||
let four_bps = (qty as u128) * 4;
|
||||
let three_bps = (qty as u128) * 3;
|
||||
let dust_qty_u64f64 = 1 << 32;
|
||||
assert!(net_bps_u64f64 + dust_qty_u64f64 > four_bps, "{:x}, {:x}, {:x}", qty, net_bps_u64f64, four_bps);
|
||||
assert!(net_bps_u64f64 + dust_qty_u64f64 > three_bps, "{:x}, {:x}, {:x}", qty, net_bps_u64f64, three_bps);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -137,6 +137,7 @@ pub enum MarketInstruction {
|
|||
/// 6. `[writable]` pc wallet
|
||||
/// 7. `[]` vault signer
|
||||
/// 8. `[]` spl token program
|
||||
/// 9. `[writable]` (optional) referrer pc wallet
|
||||
SettleFunds,
|
||||
/// 0. `[]` market
|
||||
/// 1. `[writable]` OpenOrders
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::{
|
|||
critbit::{LeafNode, NodeHandle, Slab, SlabView},
|
||||
error::DexError,
|
||||
fees::FeeTier,
|
||||
fees,
|
||||
state::{Event, EventQueue, EventView, MarketState, Request, RequestQueue, RequestView},
|
||||
};
|
||||
|
||||
|
@ -394,9 +395,13 @@ impl<'ob> OrderBookState<'ob> {
|
|||
.map_err(|_| DexErrorCode::EventQueueFull)?;
|
||||
}
|
||||
|
||||
let net_fees = native_taker_fee - accum_maker_rebates;
|
||||
let net_fees_before_referrer_rebate = native_taker_fee - accum_maker_rebates;
|
||||
let referrer_rebate = fees::referrer_rebate(native_taker_fee);
|
||||
let net_fees = net_fees_before_referrer_rebate - referrer_rebate;
|
||||
|
||||
self.market_state.referrer_rebates_accrued += referrer_rebate;
|
||||
self.market_state.pc_fees_accrued += net_fees;
|
||||
self.market_state.pc_deposits_total -= net_fees;
|
||||
self.market_state.pc_deposits_total -= net_fees + referrer_rebate;
|
||||
|
||||
if !done {
|
||||
if let Some(coin_qty_remaining) = NonZeroU64::new(unfilled_qty) {
|
||||
|
@ -605,9 +610,13 @@ impl<'ob> OrderBookState<'ob> {
|
|||
.map_err(|_| DexErrorCode::EventQueueFull)?;
|
||||
}
|
||||
|
||||
let net_fees = native_taker_fee - accum_maker_rebates;
|
||||
let net_fees_before_referrer_rebate = native_taker_fee - accum_maker_rebates;
|
||||
let referrer_rebate = fees::referrer_rebate(native_taker_fee);
|
||||
let net_fees = net_fees_before_referrer_rebate - referrer_rebate;
|
||||
|
||||
self.market_state.referrer_rebates_accrued += referrer_rebate;
|
||||
self.market_state.pc_fees_accrued += net_fees;
|
||||
self.market_state.pc_deposits_total -= net_fees;
|
||||
self.market_state.pc_deposits_total -= net_fees + referrer_rebate;
|
||||
|
||||
if !done {
|
||||
if let Some(coin_qty_remaining) = NonZeroU64::new(coin_qty_remaining) {
|
||||
|
|
136
dex/src/state.rs
136
dex/src/state.rs
|
@ -34,6 +34,7 @@ use crate::{
|
|||
critbit::Slab,
|
||||
error::{DexErrorCode, DexResult, SourceFileId},
|
||||
fees::FeeTier,
|
||||
fees,
|
||||
instruction::{
|
||||
disable_authority, fee_sweeper, msrm_token, srm_token, CancelOrderInstruction,
|
||||
InitializeMarketInstruction, MarketInstruction, NewOrderInstruction,
|
||||
|
@ -117,6 +118,8 @@ pub struct MarketState {
|
|||
|
||||
// 45
|
||||
pub fee_rate_bps: u64,
|
||||
// 46
|
||||
pub referrer_rebates_accrued: u64,
|
||||
}
|
||||
#[cfg(target_endian = "little")]
|
||||
unsafe impl Zeroable for MarketState {}
|
||||
|
@ -386,6 +389,7 @@ pub struct OpenOrders {
|
|||
pub orders: [u128; 128],
|
||||
// Using Option<NonZeroU64> in a pod type requires nightly
|
||||
pub client_order_ids: [u64; 128],
|
||||
pub referrer_rebates_accrued: u64,
|
||||
}
|
||||
unsafe impl Pod for OpenOrders {}
|
||||
unsafe impl Zeroable for OpenOrders {}
|
||||
|
@ -907,6 +911,7 @@ pub struct Event {
|
|||
order_id: u128,
|
||||
pub owner: [u64; 4],
|
||||
client_order_id: u64,
|
||||
|
||||
}
|
||||
unsafe impl Zeroable for Event {}
|
||||
unsafe impl Pod for Event {}
|
||||
|
@ -1132,6 +1137,39 @@ fn invoke_spl_token(
|
|||
)
|
||||
}
|
||||
|
||||
fn send_from_vault<'a, 'b: 'a>(
|
||||
native_amount: u64,
|
||||
recipient: account_parser::TokenAccount<'a, 'b>,
|
||||
vault: account_parser::TokenAccount<'a, 'b>,
|
||||
spl_token_program: account_parser::SplTokenProgram<'a, 'b>,
|
||||
vault_signer: account_parser::VaultSigner<'a, 'b>,
|
||||
vault_signer_seeds: &[&[u8]],
|
||||
) -> DexResult {
|
||||
let deposit_instruction = spl_token::instruction::transfer(
|
||||
&spl_token::ID,
|
||||
vault.inner().key,
|
||||
recipient.inner().key,
|
||||
&vault_signer.inner().key,
|
||||
&[],
|
||||
native_amount,
|
||||
)?;
|
||||
let accounts: &[AccountInfo] = &[
|
||||
vault.inner().clone(),
|
||||
recipient.inner().clone(),
|
||||
vault_signer.inner().clone(),
|
||||
spl_token_program.inner().clone(),
|
||||
];
|
||||
info!("invoking...");
|
||||
invoke_spl_token(
|
||||
&deposit_instruction,
|
||||
&accounts[..],
|
||||
&[vault_signer_seeds],
|
||||
)
|
||||
.map_err(|_| DexErrorCode::TransferFailed)?;
|
||||
info!("invoked");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub mod account_parser {
|
||||
use super::*;
|
||||
|
||||
|
@ -1649,6 +1687,7 @@ pub mod account_parser {
|
|||
pub pc_wallet: PcWallet<'a, 'b>,
|
||||
pub vault_signer: VaultSigner<'a, 'b>,
|
||||
pub spl_token_program: SplTokenProgram<'a, 'b>,
|
||||
pub referrer: Option<PcWallet<'a, 'b>>,
|
||||
}
|
||||
impl<'a, 'b: 'a> SettleFundsArgs<'a, 'b> {
|
||||
pub fn with_parsed_args<T>(
|
||||
|
@ -1656,8 +1695,8 @@ pub mod account_parser {
|
|||
accounts: &'a [AccountInfo<'b>],
|
||||
f: impl FnOnce(SettleFundsArgs) -> DexResult<T>,
|
||||
) -> DexResult<T> {
|
||||
check_assert_eq!(accounts.len(), 9)?;
|
||||
let &[
|
||||
check_assert!(accounts.len() == 9 || accounts.len() == 10)?;
|
||||
let (&[
|
||||
ref market_acc,
|
||||
ref open_orders_acc,
|
||||
ref owner_acc,
|
||||
|
@ -1667,7 +1706,7 @@ pub mod account_parser {
|
|||
ref pc_wallet_acc,
|
||||
ref vault_signer_acc,
|
||||
ref spl_token_program_acc,
|
||||
]: &'a [AccountInfo<'b>; 9] = array_ref![accounts, 0, 9];
|
||||
], remaining_accounts) = array_refs![accounts, 9; ..;];
|
||||
let spl_token_program = SplTokenProgram::new(spl_token_program_acc)?;
|
||||
let mut market = MarketState::load(market_acc, program_id)?;
|
||||
let owner = SignerAccount::new(owner_acc).or(check_unreachable!())?;
|
||||
|
@ -1680,6 +1719,12 @@ pub mod account_parser {
|
|||
let pc_wallet =
|
||||
PcWallet::from_account(pc_wallet_acc, &market).or(check_unreachable!())?;
|
||||
|
||||
let referrer = match remaining_accounts {
|
||||
&[] => None,
|
||||
&[ref referrer_acc] => Some(PcWallet::from_account(referrer_acc, &market).or(check_unreachable!())?),
|
||||
_ => check_unreachable!()?,
|
||||
};
|
||||
|
||||
let vault_signer = VaultSigner::new(vault_signer_acc, &market, program_id)?;
|
||||
|
||||
let mut open_orders =
|
||||
|
@ -1694,6 +1739,7 @@ pub mod account_parser {
|
|||
pc_wallet,
|
||||
vault_signer,
|
||||
spl_token_program,
|
||||
referrer,
|
||||
};
|
||||
f(args)
|
||||
}
|
||||
|
@ -1862,6 +1908,7 @@ impl State {
|
|||
pc_wallet,
|
||||
vault_signer,
|
||||
spl_token_program,
|
||||
referrer,
|
||||
} = args;
|
||||
|
||||
let native_coin_amount = open_orders.native_coin_free;
|
||||
|
@ -1899,30 +1946,35 @@ impl State {
|
|||
let market_pubkey = market.pubkey();
|
||||
let vault_signer_seeds = gen_vault_signer_seeds(&market.vault_signer_nonce, &market_pubkey);
|
||||
|
||||
for (token_amount, wallet_account, vault) in token_infos.iter() {
|
||||
let deposit_instruction = spl_token::instruction::transfer(
|
||||
&spl_token::ID,
|
||||
vault.inner().key,
|
||||
wallet_account.inner().key,
|
||||
&vault_signer.inner().key,
|
||||
&[],
|
||||
*token_amount,
|
||||
for &(token_amount, wallet_account, vault) in token_infos.iter() {
|
||||
send_from_vault(
|
||||
token_amount,
|
||||
wallet_account,
|
||||
vault,
|
||||
spl_token_program,
|
||||
vault_signer,
|
||||
&vault_signer_seeds,
|
||||
)?;
|
||||
let accounts: &[AccountInfo] = &[
|
||||
vault.inner().clone(),
|
||||
wallet_account.inner().clone(),
|
||||
vault_signer.inner().clone(),
|
||||
spl_token_program.inner().clone(),
|
||||
];
|
||||
info!("invoking...");
|
||||
invoke_spl_token(
|
||||
&deposit_instruction,
|
||||
&accounts[..],
|
||||
&[&vault_signer_seeds[..]],
|
||||
)
|
||||
.map_err(|_| DexErrorCode::TransferFailed)?;
|
||||
info!("invoked");
|
||||
}
|
||||
|
||||
match referrer {
|
||||
Some(referrer_pc_wallet) if open_orders.referrer_rebates_accrued > 0 => {
|
||||
send_from_vault(
|
||||
open_orders.referrer_rebates_accrued,
|
||||
referrer_pc_wallet.token_account(),
|
||||
pc_vault.token_account(),
|
||||
spl_token_program,
|
||||
vault_signer,
|
||||
&vault_signer_seeds,
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
market.pc_fees_accrued += open_orders.referrer_rebates_accrued;
|
||||
}
|
||||
};
|
||||
market.referrer_rebates_accrued -= open_orders.referrer_rebates_accrued;
|
||||
open_orders.referrer_rebates_accrued = 0;
|
||||
|
||||
info!("finished transfers");
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2038,6 +2090,7 @@ impl State {
|
|||
open_orders.native_pc_total -= native_qty_paid;
|
||||
open_orders.native_coin_total += native_qty_received;
|
||||
open_orders.native_coin_free += native_qty_received;
|
||||
|
||||
if maker {
|
||||
open_orders.native_pc_free += native_fee_or_rebate;
|
||||
}
|
||||
|
@ -2048,6 +2101,9 @@ impl State {
|
|||
open_orders.native_pc_free += native_qty_received;
|
||||
}
|
||||
};
|
||||
if !maker {
|
||||
open_orders.referrer_rebates_accrued += fees::referrer_rebate(native_fee_or_rebate);
|
||||
}
|
||||
if let Some(client_id) = client_order_id {
|
||||
debug_assert_eq!(
|
||||
client_id.get(),
|
||||
|
@ -2248,32 +2304,17 @@ impl State {
|
|||
} = args;
|
||||
let token_amount = market.pc_fees_accrued;
|
||||
market.pc_fees_accrued = 0;
|
||||
let deposit_instruction = spl_token::instruction::transfer(
|
||||
&spl_token::ID,
|
||||
pc_vault.account().key,
|
||||
fee_receiver.account().key,
|
||||
&vault_signer.inner().key,
|
||||
&[],
|
||||
token_amount,
|
||||
)?;
|
||||
|
||||
let market_pubkey = market.pubkey();
|
||||
let vault_signer_seeds = gen_vault_signer_seeds(&market.vault_signer_nonce, &market_pubkey);
|
||||
let accounts: &[AccountInfo] = &[
|
||||
pc_vault.account().clone(),
|
||||
fee_receiver.account().clone(),
|
||||
vault_signer.inner().clone(),
|
||||
spl_token_program.inner().clone(),
|
||||
];
|
||||
info!("invoking...");
|
||||
invoke_spl_token(
|
||||
&deposit_instruction,
|
||||
&accounts[..],
|
||||
&[&vault_signer_seeds[..]],
|
||||
send_from_vault(
|
||||
token_amount,
|
||||
fee_receiver.token_account(),
|
||||
pc_vault.token_account(),
|
||||
spl_token_program,
|
||||
vault_signer,
|
||||
&vault_signer_seeds,
|
||||
)
|
||||
.map_err(|_| DexErrorCode::TransferFailed)?;
|
||||
info!("invoked");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
|
@ -2377,6 +2418,7 @@ impl State {
|
|||
|
||||
pc_dust_threshold,
|
||||
fee_rate_bps: fee_rate_bps as u64,
|
||||
referrer_rebates_accrued: 0,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -348,7 +348,8 @@ fn test_new_order() {
|
|||
.unwrap();
|
||||
{
|
||||
let market = MarketState::load(&accounts.market, &dex_program_id).unwrap();
|
||||
assert_eq!(market.pc_fees_accrued, 760);
|
||||
assert_eq!(market.referrer_rebates_accrued, 176);
|
||||
assert_eq!(market.pc_fees_accrued, 584);
|
||||
assert_eq!(market.pc_deposits_total, 500_340);
|
||||
}
|
||||
{
|
||||
|
@ -387,7 +388,8 @@ fn test_new_order() {
|
|||
|
||||
{
|
||||
let market = MarketState::load(&accounts.market, &dex_program_id).unwrap();
|
||||
assert_eq!(market.pc_fees_accrued, 760);
|
||||
assert_eq!(market.referrer_rebates_accrued, 176);
|
||||
assert_eq!(market.pc_fees_accrued, 584);
|
||||
assert_eq!(market.pc_deposits_total, 500_340);
|
||||
}
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue