Skip to content

Commit

Permalink
Merge pull request #42 from adm-metaex/penalties/decrease-rewards
Browse files Browse the repository at this point in the history
[mtg-577] Add decrease rewards penalty instruction
  • Loading branch information
kstepanovdev authored Sep 6, 2024
2 parents 53599f4 + 08f2676 commit efbce88
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 1 deletion.
6 changes: 6 additions & 0 deletions programs/rewards/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ pub enum MplxRewardsError {
/// Withdrawal is restricted while claiming is restricted
#[error("Withdrawal is restricted while claiming is restricted")]
WithdrawalRestricted,

/// 25
#[error(
"Rewards: Penalty is not apliable becase it's bigger than the mining's weighted stake"
)]
DecreaseRewardsTooBig,
}

impl PrintProgramError for MplxRewardsError {
Expand Down
33 changes: 33 additions & 0 deletions programs/rewards/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,15 @@ pub enum RewardsInstruction {
// None if it's Flex period, because it's already expired
stake_expiration_date: Option<u64>,
},

#[account(0, signer, name = "deposit_authority", desc = "The address of the Staking program's Registrar, which is PDA and is responsible for signing CPIs")]
#[account(1, writable, name = "reward_pool", desc = "The address of the reward pool")]
#[account(2, writable, name = "mining", desc = "The address of the mining account which belongs to the user and stores info about user's rewards")]
DecreaseRewards {
mining_owner: Pubkey,
// The number by which weighted stake should be decreased
decreased_weighted_stake_number: u64,
},
}

/// Creates 'InitializePool' instruction.
Expand Down Expand Up @@ -564,3 +573,27 @@ pub fn slash(
accounts,
)
}

pub fn decrease_rewards(
program_id: &Pubkey,
deposit_authority: &Pubkey,
reward_pool: &Pubkey,
mining: &Pubkey,
mining_owner: &Pubkey,
decreased_weighted_stake_number: u64,
) -> Instruction {
let accounts = vec![
AccountMeta::new_readonly(*deposit_authority, true),
AccountMeta::new(*reward_pool, false),
AccountMeta::new(*mining, false),
];

Instruction::new_with_borsh(
*program_id,
&RewardsInstruction::DecreaseRewards {
mining_owner: *mining_owner,
decreased_weighted_stake_number,
},
accounts,
)
}
12 changes: 12 additions & 0 deletions programs/rewards/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,17 @@ pub fn process_instruction<'a>(
stake_expiration_date,
)
}
RewardsInstruction::DecreaseRewards {
mining_owner,
decreased_weighted_stake_number,
} => {
msg!("RewardsInstruction: DecreaseRewards");
process_decrease_rewards(
program_id,
accounts,
&mining_owner,
decreased_weighted_stake_number,
)
}
}
}
32 changes: 32 additions & 0 deletions programs/rewards/src/instructions/penalties/decrease_rewards.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::{asserts::assert_and_get_pool_and_mining, utils::AccountLoader};
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};

pub fn process_decrease_rewards<'a>(
program_id: &Pubkey,
accounts: &'a [AccountInfo<'a>],
mining_owner: &Pubkey,
decreased_weighted_stake_number: u64,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter().enumerate();

let deposit_authority = AccountLoader::next_signer(account_info_iter)?;
let reward_pool = AccountLoader::next_with_owner(account_info_iter, program_id)?;
let mining = AccountLoader::next_with_owner(account_info_iter, program_id)?;

let reward_pool_data = &mut reward_pool.data.borrow_mut();
let mining_data = &mut mining.data.borrow_mut();

let (_, mut wrapped_mining) = assert_and_get_pool_and_mining(
program_id,
mining_owner,
mining,
reward_pool,
deposit_authority,
reward_pool_data,
mining_data,
)?;

wrapped_mining.decrease_rewards(decreased_weighted_stake_number)?;

Ok(())
}
2 changes: 2 additions & 0 deletions programs/rewards/src/instructions/penalties/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
mod allow_tokenflow;
mod decrease_rewards;
mod restrict_batch_minting;
mod restrict_tokenflow;
mod slash;

pub(crate) use allow_tokenflow::*;
pub(crate) use decrease_rewards::*;
pub(crate) use restrict_batch_minting::*;
pub(crate) use restrict_tokenflow::*;
pub(crate) use slash::*;
107 changes: 106 additions & 1 deletion programs/rewards/src/state/mining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,39 @@ impl<'a> WrappedMining<'a> {

Ok(())
}

/// Decrease rewards
pub fn decrease_rewards(&mut self, mut decreased_weighted_stake_number: u64) -> ProgramResult {
if decreased_weighted_stake_number == 0 {
return Ok(());
}

if decreased_weighted_stake_number > self.mining.share {
return Err(MplxRewardsError::DecreaseRewardsTooBig.into());
}

// apply penalty to the weighted stake
self.mining.share = self
.mining
.share
.safe_sub(decreased_weighted_stake_number)?;

// going through the weighted stake diffs backwards
// and decreasing the modifiers accordingly to the decreased share number.
// otherwise moddifier might decrease the share more then needed, even to negative value.
for (_, stake_diff) in self.weighted_stake_diffs.iter_mut().rev() {
if stake_diff >= &mut decreased_weighted_stake_number {
*stake_diff = stake_diff.safe_sub(decreased_weighted_stake_number)?;
break;
} else {
decreased_weighted_stake_number =
decreased_weighted_stake_number.safe_sub(*stake_diff)?;
*stake_diff = 0;
}
}

Ok(())
}
}

#[repr(C)]
Expand Down Expand Up @@ -257,8 +290,11 @@ impl<'a> WrappedImmutableMining<'a> {
})
}
}

#[allow(unused_imports)]
mod test {
#[allow(unused_imports)]
use super::*;

#[test]
fn test_wrapped_immutable_mining_is_same_size_as_wrapped_mining() {
assert_eq!(
Expand Down Expand Up @@ -306,4 +342,73 @@ mod test {
);
assert_eq!(wrapped_immutable_mining.mining.bump, bump);
}

#[test]
fn slighly_decrease_rewards() {
let mut wrapped_mining = super::WrappedMining {
mining: &mut super::Mining {
share: 3600,
..Default::default()
},
weighted_stake_diffs: &mut Default::default(),
};
// three stakes:
// - 500 x4 (six months)
// - 700 x2 (three months)
// - 200 x1 (flex)
wrapped_mining.weighted_stake_diffs.insert(365, 1500);
wrapped_mining.weighted_stake_diffs.insert(180, 700);

wrapped_mining.decrease_rewards(300).unwrap();

assert_eq!(wrapped_mining.mining.share, 3300);
assert_eq!(wrapped_mining.weighted_stake_diffs.get(&365), Some(&1200));
assert_eq!(wrapped_mining.weighted_stake_diffs.get(&180), Some(&700));
}

#[test]
fn moderate_decrease_rewards() {
let mut wrapped_mining = super::WrappedMining {
mining: &mut super::Mining {
share: 3600,
..Default::default()
},
weighted_stake_diffs: &mut Default::default(),
};
// three stakes:
// - 500 x4 (six months)
// - 700 x2 (three months)
// - 200 x1 (flex)
wrapped_mining.weighted_stake_diffs.insert(365, 1500);
wrapped_mining.weighted_stake_diffs.insert(180, 700);

wrapped_mining.decrease_rewards(2200).unwrap();

assert_eq!(wrapped_mining.mining.share, 1400);
assert_eq!(wrapped_mining.weighted_stake_diffs.get(&365), Some(&0));
assert_eq!(wrapped_mining.weighted_stake_diffs.get(&180), Some(&0));
}

#[test]
fn severe_decrease_rewards() {
let mut wrapped_mining = super::WrappedMining {
mining: &mut super::Mining {
share: 3600,
..Default::default()
},
weighted_stake_diffs: &mut Default::default(),
};
// three stakes:
// - 500 x4 (six months)
// - 700 x2 (three months)
// - 200 x1 (flex)
wrapped_mining.weighted_stake_diffs.insert(365, 1500);
wrapped_mining.weighted_stake_diffs.insert(180, 700);

wrapped_mining.decrease_rewards(3500).unwrap();

assert_eq!(wrapped_mining.mining.share, 100);
assert_eq!(wrapped_mining.weighted_stake_diffs.get(&365), Some(&0));
assert_eq!(wrapped_mining.weighted_stake_diffs.get(&180), Some(&0));
}
}
25 changes: 25 additions & 0 deletions programs/rewards/tests/rewards/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,31 @@ impl TestRewards {
context.banks_client.process_transaction(tx).await
}

#[allow(dead_code)]
pub async fn decrease_rewards(
&self,
context: &mut ProgramTestContext,
mining_account: &Pubkey,
mining_owner: &Pubkey,
decreased_weighted_stake_number: u64,
) -> BanksClientResult<()> {
let tx = Transaction::new_signed_with_payer(
&[mplx_rewards::instruction::decrease_rewards(
&mplx_rewards::id(),
&self.deposit_authority.pubkey(),
&self.reward_pool.pubkey(),
mining_account,
mining_owner,
decreased_weighted_stake_number,
)],
Some(&context.payer.pubkey()),
&[&context.payer, &self.deposit_authority],
context.last_blockhash,
);

context.banks_client.process_transaction(tx).await
}

pub async fn restrict_tokenflow(
&self,
context: &mut ProgramTestContext,
Expand Down

0 comments on commit efbce88

Please sign in to comment.