From fa4156099123fae604ef71d3883a4d9083ef78d7 Mon Sep 17 00:00:00 2001 From: Christian Krueger Date: Fri, 29 Nov 2024 17:12:51 -0600 Subject: [PATCH] looking much better --- core/src/base_reward_router.rs | 245 +++++++-------- core/src/discriminators.rs | 2 +- core/src/fees.rs | 20 +- core/src/ncn_reward_router.rs | 281 +++++++++--------- .../src/process_operator_epoch_reward_pool.rs | 4 +- 5 files changed, 281 insertions(+), 271 deletions(-) diff --git a/core/src/base_reward_router.rs b/core/src/base_reward_router.rs index 9d4b666..5183404 100644 --- a/core/src/base_reward_router.rs +++ b/core/src/base_reward_router.rs @@ -5,108 +5,11 @@ use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError use spl_math::precise_number::PreciseNumber; use crate::{ - ballot_box::BallotBox, - base_fee_group::BaseFeeGroup, - discriminators::Discriminators, - error::TipRouterError, - fees::{FeeConfig, Fees}, - ncn_fee_group::NcnFeeGroup, + ballot_box::BallotBox, base_fee_group::BaseFeeGroup, discriminators::Discriminators, + error::TipRouterError, fees::Fees, ncn_fee_group::NcnFeeGroup, ncn_reward_router::NcnRewardRouter, }; -#[derive(Default, Debug, Clone, PartialEq, Eq, Copy, Zeroable, ShankType, Pod)] -#[repr(C)] -pub struct Rewards { - rewards: PodU64, -} - -impl Rewards { - pub fn rewards(self) -> u64 { - self.rewards.into() - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Copy, Zeroable, ShankType, Pod)] -#[repr(C)] -pub struct NcnRewardRoute { - operator: Pubkey, - ncn_fee_group_rewards: [Rewards; 8], -} - -impl Default for NcnRewardRoute { - fn default() -> Self { - Self { - operator: Pubkey::default(), - ncn_fee_group_rewards: [Rewards::default(); NcnFeeGroup::FEE_GROUP_COUNT], - } - } -} - -impl NcnRewardRoute { - pub fn new( - operator: Pubkey, - ncn_fee_group: NcnFeeGroup, - rewards: u64, - ) -> Result { - let mut route = Self { - operator, - ncn_fee_group_rewards: [Rewards::default(); NcnFeeGroup::FEE_GROUP_COUNT], - }; - - route.set_rewards(ncn_fee_group, rewards)?; - - Ok(route) - } - - pub const fn operator(&self) -> Pubkey { - self.operator - } - - pub fn rewards(&self, ncn_fee_group: NcnFeeGroup) -> Result { - let group_index = ncn_fee_group.group_index()?; - Ok(self.ncn_fee_group_rewards[group_index].rewards()) - } - - fn set_rewards( - &mut self, - ncn_fee_group: NcnFeeGroup, - rewards: u64, - ) -> Result<(), TipRouterError> { - let group_index = ncn_fee_group.group_index()?; - self.ncn_fee_group_rewards[group_index].rewards = PodU64::from(rewards); - - Ok(()) - } - - pub fn increment_rewards( - &mut self, - ncn_fee_group: NcnFeeGroup, - rewards: u64, - ) -> Result<(), TipRouterError> { - let current_rewards = self.rewards(ncn_fee_group)?; - - let new_rewards = current_rewards - .checked_add(rewards) - .ok_or(TipRouterError::ArithmeticOverflow)?; - - self.set_rewards(ncn_fee_group, new_rewards) - } - - pub fn decrement_rewards( - &mut self, - ncn_fee_group: NcnFeeGroup, - rewards: u64, - ) -> Result<(), TipRouterError> { - let current_rewards = self.rewards(ncn_fee_group)?; - - let new_rewards = current_rewards - .checked_sub(rewards) - .ok_or(TipRouterError::ArithmeticOverflow)?; - - self.set_rewards(ncn_fee_group, new_rewards) - } -} - // PDA'd ["epoch_reward_router", NCN, NCN_EPOCH_SLOT] #[derive(Debug, Clone, Copy, Zeroable, ShankType, Pod, AccountDeserialize, ShankAccount)] #[repr(C)] @@ -125,15 +28,15 @@ pub struct BaseRewardRouter { reserved: [u8; 128], - base_fee_group_rewards: [Rewards; 8], - ncn_fee_group_rewards: [Rewards; 8], + base_fee_group_rewards: [BaseRewardRouterRewards; 8], + ncn_fee_group_rewards: [BaseRewardRouterRewards; 8], //TODO change to 256 ncn_fee_group_reward_routes: [NcnRewardRoute; 32], } impl Discriminator for BaseRewardRouter { - const DISCRIMINATOR: u8 = Discriminators::EpochRewardRouter as u8; + const DISCRIMINATOR: u8 = Discriminators::BaseRewardRouter as u8; } impl BaseRewardRouter { @@ -146,8 +49,10 @@ impl BaseRewardRouter { reward_pool: PodU64::from(0), rewards_processed: PodU64::from(0), reserved: [0; 128], - base_fee_group_rewards: [Rewards::default(); NcnFeeGroup::FEE_GROUP_COUNT], - ncn_fee_group_rewards: [Rewards::default(); NcnFeeGroup::FEE_GROUP_COUNT], + base_fee_group_rewards: [BaseRewardRouterRewards::default(); + NcnFeeGroup::FEE_GROUP_COUNT], + ncn_fee_group_rewards: [BaseRewardRouterRewards::default(); + NcnFeeGroup::FEE_GROUP_COUNT], ncn_fee_group_reward_routes: [NcnRewardRoute::default(); 32], } } @@ -275,11 +180,11 @@ impl BaseRewardRouter { let rewards_to_process = self.ncn_fee_group_rewards(*group)?; let winning_reward_stake_weight = winning_stake_weight.ncn_fee_group_stake_weight(*group)?; - let operator_reward_stake_weight = + let ncn_route_reward_stake_weight = votes.stake_weight().ncn_fee_group_stake_weight(*group)?; let ncn_fee_group_route_reward = Self::calculate_ncn_fee_group_route_reward( - operator_reward_stake_weight, + ncn_route_reward_stake_weight, winning_reward_stake_weight, rewards_to_process, )?; @@ -345,41 +250,42 @@ impl BaseRewardRouter { } fn calculate_ncn_fee_group_route_reward( - operator_reward_stake_weight: u128, + ncn_route_reward_stake_weight: u128, winning_reward_stake_weight: u128, rewards_to_process: u64, ) -> Result { - if operator_reward_stake_weight == 0 || rewards_to_process == 0 { + if ncn_route_reward_stake_weight == 0 || rewards_to_process == 0 { return Ok(0); } let precise_rewards_to_process = PreciseNumber::new(rewards_to_process as u128) .ok_or(TipRouterError::NewPreciseNumberError)?; - let precise_operator_reward_stake_weight = PreciseNumber::new(operator_reward_stake_weight) - .ok_or(TipRouterError::NewPreciseNumberError)?; + let precise_ncn_route_reward_stake_weight = + PreciseNumber::new(ncn_route_reward_stake_weight) + .ok_or(TipRouterError::NewPreciseNumberError)?; let precise_winning_reward_stake_weight = PreciseNumber::new(winning_reward_stake_weight) .ok_or(TipRouterError::NewPreciseNumberError)?; - let precise_operator_reward = precise_rewards_to_process - .checked_mul(&precise_operator_reward_stake_weight) + let precise_ncn_route_reward = precise_rewards_to_process + .checked_mul(&precise_ncn_route_reward_stake_weight) .and_then(|x| x.checked_div(&precise_winning_reward_stake_weight)) .ok_or(TipRouterError::ArithmeticOverflow)?; - let floored_precise_operator_reward = precise_operator_reward + let floored_precise_ncn_route_reward = precise_ncn_route_reward .floor() .ok_or(TipRouterError::ArithmeticFloorError)?; - let operator_reward_u128: u128 = floored_precise_operator_reward + let ncn_route_reward_u128: u128 = floored_precise_ncn_route_reward .to_imprecise() .ok_or(TipRouterError::CastToImpreciseNumberError)?; - let operator_reward: u64 = operator_reward_u128 + let ncn_route_reward: u64 = ncn_route_reward_u128 .try_into() .map_err(|_| TipRouterError::CastToU64Error)?; - Ok(operator_reward) + Ok(ncn_route_reward) } // ------------------ REWARD TALLIES --------------------- @@ -561,16 +467,16 @@ impl BaseRewardRouter { return Ok(()); } - for operator_reward in self.ncn_fee_group_reward_routes.iter_mut() { - if operator_reward.operator == operator { - operator_reward.increment_rewards(ncn_fee_group, rewards)?; + for ncn_route_reward in self.ncn_fee_group_reward_routes.iter_mut() { + if ncn_route_reward.operator == operator { + ncn_route_reward.increment_rewards(ncn_fee_group, rewards)?; return Ok(()); } } - for operator_reward in self.ncn_fee_group_reward_routes.iter_mut() { - if operator_reward.operator == Pubkey::default() { - *operator_reward = NcnRewardRoute::new(operator, ncn_fee_group, rewards)?; + for ncn_route_reward in self.ncn_fee_group_reward_routes.iter_mut() { + if ncn_route_reward.operator == Pubkey::default() { + *ncn_route_reward = NcnRewardRoute::new(operator, ncn_fee_group, rewards)?; return Ok(()); } } @@ -600,3 +506,98 @@ impl BaseRewardRouter { Err(TipRouterError::OperatorRewardNotFound) } } + +#[derive(Debug, Clone, PartialEq, Eq, Copy, Zeroable, ShankType, Pod)] +#[repr(C)] +pub struct NcnRewardRoute { + operator: Pubkey, + ncn_fee_group_rewards: [BaseRewardRouterRewards; 8], +} + +impl Default for NcnRewardRoute { + fn default() -> Self { + Self { + operator: Pubkey::default(), + ncn_fee_group_rewards: [BaseRewardRouterRewards::default(); + NcnFeeGroup::FEE_GROUP_COUNT], + } + } +} + +impl NcnRewardRoute { + pub fn new( + operator: Pubkey, + ncn_fee_group: NcnFeeGroup, + rewards: u64, + ) -> Result { + let mut route = Self { + operator, + ncn_fee_group_rewards: [BaseRewardRouterRewards::default(); + NcnFeeGroup::FEE_GROUP_COUNT], + }; + + route.set_rewards(ncn_fee_group, rewards)?; + + Ok(route) + } + + pub const fn operator(&self) -> Pubkey { + self.operator + } + + pub fn rewards(&self, ncn_fee_group: NcnFeeGroup) -> Result { + let group_index = ncn_fee_group.group_index()?; + Ok(self.ncn_fee_group_rewards[group_index].rewards()) + } + + fn set_rewards( + &mut self, + ncn_fee_group: NcnFeeGroup, + rewards: u64, + ) -> Result<(), TipRouterError> { + let group_index = ncn_fee_group.group_index()?; + self.ncn_fee_group_rewards[group_index].rewards = PodU64::from(rewards); + + Ok(()) + } + + pub fn increment_rewards( + &mut self, + ncn_fee_group: NcnFeeGroup, + rewards: u64, + ) -> Result<(), TipRouterError> { + let current_rewards = self.rewards(ncn_fee_group)?; + + let new_rewards = current_rewards + .checked_add(rewards) + .ok_or(TipRouterError::ArithmeticOverflow)?; + + self.set_rewards(ncn_fee_group, new_rewards) + } + + pub fn decrement_rewards( + &mut self, + ncn_fee_group: NcnFeeGroup, + rewards: u64, + ) -> Result<(), TipRouterError> { + let current_rewards = self.rewards(ncn_fee_group)?; + + let new_rewards = current_rewards + .checked_sub(rewards) + .ok_or(TipRouterError::ArithmeticOverflow)?; + + self.set_rewards(ncn_fee_group, new_rewards) + } +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, Copy, Zeroable, ShankType, Pod)] +#[repr(C)] +pub struct BaseRewardRouterRewards { + rewards: PodU64, +} + +impl BaseRewardRouterRewards { + pub fn rewards(self) -> u64 { + self.rewards.into() + } +} diff --git a/core/src/discriminators.rs b/core/src/discriminators.rs index 7b6a13b..2602fd3 100644 --- a/core/src/discriminators.rs +++ b/core/src/discriminators.rs @@ -10,5 +10,5 @@ pub enum Discriminators { // Voting BallotBox = 0x20, // Distribution - EpochRewardRouter = 0x30, + BaseRewardRouter = 0x30, } diff --git a/core/src/fees.rs b/core/src/fees.rs index 48358f0..78ef32a 100644 --- a/core/src/fees.rs +++ b/core/src/fees.rs @@ -5,10 +5,8 @@ use solana_program::pubkey::Pubkey; use spl_math::precise_number::PreciseNumber; use crate::{ - base_fee_group::{self, BaseFeeGroup}, - constants::MAX_FEE_BPS, - error::TipRouterError, - ncn_fee_group::{self, NcnFeeGroup}, + base_fee_group::BaseFeeGroup, constants::MAX_FEE_BPS, error::TipRouterError, + ncn_fee_group::NcnFeeGroup, }; /// Fee Config. Allows for fee updates to take place in a future epoch without requiring an update. @@ -249,10 +247,6 @@ impl FeeConfig { // ------------- Setters ------------- /// Updates the Fee Config - /// Any option set to None will be ignored - /// `new_wallet`` and `new_block_engine_fee_bps` will take effect immediately - /// `new_ncn_fee_bps` will set the fee group specified in `new_ncn_fee_group` - /// if no `new_ncn_fee_group` is specified, the default ncn group will be set pub fn update_fee_config( &mut self, new_block_engine_fee_bps: Option, @@ -276,11 +270,11 @@ impl FeeConfig { }; if let Some(new_base_fee_wallet) = new_base_fee_wallet { - self.set_base_fee_wallet(base_fee_group, new_base_fee_wallet); + self.set_base_fee_wallet(base_fee_group, new_base_fee_wallet)?; } if let Some(new_base_fee_bps) = new_base_fee_bps { - self.set_base_fee_bps(base_fee_group, new_base_fee_bps, current_epoch); + self.set_base_fee_bps(base_fee_group, new_base_fee_bps, current_epoch)?; } // NCN FEE @@ -291,7 +285,7 @@ impl FeeConfig { }; if let Some(new_ncn_fee_bps) = new_ncn_fee_bps { - self.set_ncn_fee_bps(ncn_fee_group, new_ncn_fee_bps, current_epoch); + self.set_ncn_fee_bps(ncn_fee_group, new_ncn_fee_bps, current_epoch)?; } // ACTIVATION EPOCH @@ -511,9 +505,9 @@ impl Fee { #[cfg(test)] mod tests { - use solana_program::pubkey::Pubkey; + // use solana_program::pubkey::Pubkey; - use super::*; + // use super::*; // #[test] // fn test_update_fees() { diff --git a/core/src/ncn_reward_router.rs b/core/src/ncn_reward_router.rs index 6aed7a0..1f39955 100644 --- a/core/src/ncn_reward_router.rs +++ b/core/src/ncn_reward_router.rs @@ -10,56 +10,6 @@ use crate::{ ncn_fee_group::NcnFeeGroup, }; -#[derive(Debug, Clone, PartialEq, Eq, Copy, Zeroable, ShankType, Pod, ShankType)] -#[repr(C)] -pub struct RewardRoutes { - destination: Pubkey, - rewards: PodU64, - reserved: [u8; 128], -} - -impl Default for RewardRoutes { - fn default() -> Self { - Self { - destination: Pubkey::default(), - rewards: PodU64::from(0), - reserved: [0; 128], - } - } -} - -impl RewardRoutes { - pub const fn destination(&self) -> Pubkey { - self.destination - } - - pub fn set_destination(&mut self, destination: Pubkey) { - self.destination = destination; - } - - pub fn rewards(&self) -> u64 { - self.rewards.into() - } - - pub fn increment_rewards(&mut self, rewards: u64) -> Result<(), TipRouterError> { - self.rewards = PodU64::from( - self.rewards() - .checked_add(rewards) - .ok_or(TipRouterError::ArithmeticOverflow)?, - ); - Ok(()) - } - - pub fn decrement_rewards(&mut self, rewards: u64) -> Result<(), TipRouterError> { - self.rewards = PodU64::from( - self.rewards() - .checked_sub(rewards) - .ok_or(TipRouterError::ArithmeticUnderflowError)?, - ); - Ok(()) - } -} - // PDA'd ["epoch_reward_router", NCN, NCN_EPOCH_SLOT] #[derive(Debug, Clone, Copy, Zeroable, ShankType, Pod, AccountDeserialize, ShankAccount)] #[repr(C)] @@ -85,11 +35,11 @@ pub struct NcnRewardRouter { reserved: [u8; 128], //TODO change to 64 - vault_rewards: [RewardRoutes; 32], + vault_reward_routes: [VaultRewardRoute; 32], } impl Discriminator for NcnRewardRouter { - const DISCRIMINATOR: u8 = Discriminators::EpochRewardRouter as u8; + const DISCRIMINATOR: u8 = Discriminators::BaseRewardRouter as u8; } impl NcnRewardRouter { @@ -108,11 +58,11 @@ impl NcnRewardRouter { ncn_epoch: PodU64::from(ncn_epoch), bump, slot_created: PodU64::from(slot_created), - vault_rewards: [RewardRoutes::default(); 32], reward_pool: PodU64::from(0), rewards_processed: PodU64::from(0), operator_rewards: PodU64::from(0), reserved: [0; 128], + vault_reward_routes: [VaultRewardRoute::default(); 32], } } @@ -124,7 +74,7 @@ impl NcnRewardRouter { ) -> Vec> { Vec::from_iter( [ - b"operator_epoch_reward_router".to_vec(), + b"ncn_reward_router".to_vec(), vec![ncn_fee_group.group], operator.to_bytes().to_vec(), ncn.to_bytes().to_vec(), @@ -158,19 +108,19 @@ impl NcnRewardRouter { expect_writable: bool, ) -> Result<(), ProgramError> { if account.owner.ne(program_id) { - msg!("Epoch Reward Router account has an invalid owner"); + msg!("NCN Reward Router account has an invalid owner"); return Err(ProgramError::InvalidAccountOwner); } if account.data_is_empty() { - msg!("Epoch Reward Router account data is empty"); + msg!("NCN Reward Router account data is empty"); return Err(ProgramError::InvalidAccountData); } if expect_writable && !account.is_writable { - msg!("Epoch Reward Router account is not writable"); + msg!("NCN Reward Router account is not writable"); return Err(ProgramError::InvalidAccountData); } if account.data.borrow()[0].ne(&Self::DISCRIMINATOR) { - msg!("Epoch Reward Router account discriminator is invalid"); + msg!("NCN Reward Router account discriminator is invalid"); return Err(ProgramError::InvalidAccountData); } if account.key.ne(&Self::find_program_address( @@ -182,25 +132,26 @@ impl NcnRewardRouter { ) .0) { - msg!("Epoch Reward Router account is not at the correct PDA"); + msg!("NCN Reward Router account is not at the correct PDA"); return Err(ProgramError::InvalidAccountData); } Ok(()) } - pub fn process_incoming_rewards(&mut self, account_balance: u64) -> Result<(), TipRouterError> { + // ------------------------ ROUTING ------------------------ + pub fn route_incoming_rewards(&mut self, account_balance: u64) -> Result<(), TipRouterError> { let total_rewards = self.total_rewards()?; let incoming_rewards = account_balance .checked_sub(total_rewards) .ok_or(TipRouterError::ArithmeticUnderflowError)?; - self.increment_reward_pool(incoming_rewards)?; + self.route_to_reward_pool(incoming_rewards)?; Ok(()) } - pub fn process_reward_pool( + pub fn route_reward_pool( &mut self, operator_snapshot: &OperatorSnapshot, ) -> Result<(), TipRouterError> { @@ -212,8 +163,8 @@ impl NcnRewardRouter { let operator_rewards = Self::calculate_operator_reward(operator_fee_bps as u64, rewards_to_process)?; - self.increment_operator_rewards(operator_rewards)?; - self.decrement_reward_pool(operator_rewards)?; + self.route_from_reward_pool(operator_rewards)?; + self.route_to_operator_rewards(operator_rewards)?; } // Vault Rewards @@ -239,8 +190,8 @@ impl NcnRewardRouter { rewards_to_process, )?; - self.insert_or_increment_vault_rewards(vault, vault_reward)?; - self.decrement_reward_pool(vault_reward)?; + self.route_from_reward_pool(vault_reward)?; + self.route_to_vault_reward_route(vault, vault_reward)?; } } @@ -248,13 +199,14 @@ impl NcnRewardRouter { { let leftover_rewards = self.reward_pool(); - self.increment_operator_rewards(leftover_rewards)?; - self.decrement_reward_pool(leftover_rewards)?; + self.route_from_reward_pool(leftover_rewards)?; + self.route_to_operator_rewards(leftover_rewards)?; } Ok(()) } + // ------------------------ CALCULATIONS ------------------------ fn calculate_operator_reward( fee_bps: u64, rewards_to_process: u64, @@ -329,58 +281,7 @@ impl NcnRewardRouter { Ok(operator_reward) } - pub fn insert_or_increment_vault_rewards( - &mut self, - vault: Pubkey, - rewards: u64, - ) -> Result<(), TipRouterError> { - if rewards == 0 { - return Ok(()); - } - - for vault_reward in self.vault_rewards.iter_mut() { - if vault_reward.destination() == vault { - vault_reward.increment_rewards(rewards)?; - return Ok(()); - } - } - - for vault_reward in self.vault_rewards.iter_mut() { - if vault_reward.destination() == Pubkey::default() { - vault_reward.set_destination(vault); - vault_reward.increment_rewards(rewards)?; - return Ok(()); - } - } - - Err(TipRouterError::OperatorRewardListFull) - } - - pub fn decrement_vault_rewards( - &mut self, - vault: Pubkey, - rewards: u64, - ) -> Result<(), TipRouterError> { - if rewards == 0 { - return Ok(()); - } - - for operator_reward in self.vault_rewards.iter_mut() { - if operator_reward.destination() == vault { - operator_reward.decrement_rewards(rewards)?; - return Ok(()); - } - } - - self.rewards_processed = PodU64::from( - self.rewards_processed() - .checked_sub(rewards) - .ok_or(TipRouterError::ArithmeticUnderflowError)?, - ); - - Err(TipRouterError::OperatorRewardNotFound) - } - + // ------------------------ REWARD POOL ------------------------ pub fn total_rewards(&self) -> Result { let total_rewards = self .reward_pool() @@ -390,15 +291,11 @@ impl NcnRewardRouter { Ok(total_rewards) } - pub fn rewards_processed(&self) -> u64 { - self.rewards_processed.into() - } - pub fn reward_pool(&self) -> u64 { self.reward_pool.into() } - pub fn increment_reward_pool(&mut self, rewards: u64) -> Result<(), TipRouterError> { + pub fn route_to_reward_pool(&mut self, rewards: u64) -> Result<(), TipRouterError> { if rewards == 0 { return Ok(()); } @@ -411,7 +308,7 @@ impl NcnRewardRouter { Ok(()) } - pub fn decrement_reward_pool(&mut self, rewards: u64) -> Result<(), TipRouterError> { + pub fn route_from_reward_pool(&mut self, rewards: u64) -> Result<(), TipRouterError> { if rewards == 0 { return Ok(()); } @@ -422,20 +319,47 @@ impl NcnRewardRouter { .ok_or(TipRouterError::ArithmeticUnderflowError)?, ); + Ok(()) + } + + // ------------------------ REWARDS PROCESSED ------------------------ + pub fn rewards_processed(&self) -> u64 { + self.rewards_processed.into() + } + + pub fn increment_rewards_processed(&mut self, rewards: u64) -> Result<(), TipRouterError> { + if rewards == 0 { + return Ok(()); + } + self.rewards_processed = PodU64::from( self.rewards_processed() .checked_add(rewards) .ok_or(TipRouterError::ArithmeticOverflow)?, ); + Ok(()) + } + + pub fn decrement_rewards_processed(&mut self, rewards: u64) -> Result<(), TipRouterError> { + if rewards == 0 { + return Ok(()); + } + self.rewards_processed = PodU64::from( + self.rewards_processed() + .checked_sub(rewards) + .ok_or(TipRouterError::ArithmeticUnderflowError)?, + ); Ok(()) } + // ------------------------ OPERATOR REWARDS ------------------------ + pub fn operator_rewards(&self) -> u64 { self.operator_rewards.into() } - pub fn increment_operator_rewards(&mut self, rewards: u64) -> Result<(), TipRouterError> { + pub fn route_to_operator_rewards(&mut self, rewards: u64) -> Result<(), TipRouterError> { if rewards == 0 { return Ok(()); } @@ -448,7 +372,7 @@ impl NcnRewardRouter { Ok(()) } - pub fn decrement_operator_rewards(&mut self, rewards: u64) -> Result<(), TipRouterError> { + pub fn distribute_operator_rewards(&mut self, rewards: u64) -> Result<(), TipRouterError> { if rewards == 0 { return Ok(()); } @@ -459,11 +383,102 @@ impl NcnRewardRouter { .ok_or(TipRouterError::ArithmeticUnderflowError)?, ); - self.rewards_processed = PodU64::from( - self.rewards_processed() - .checked_sub(rewards) - .ok_or(TipRouterError::ArithmeticUnderflowError)?, - ); + self.decrement_rewards_processed(rewards)?; Ok(()) } + + // ------------------------ VAULT REWARD ROUTES ------------------------ + pub fn route_to_vault_reward_route( + &mut self, + vault: Pubkey, + rewards: u64, + ) -> Result<(), TipRouterError> { + if rewards == 0 { + return Ok(()); + } + + for vault_reward in self.vault_reward_routes.iter_mut() { + if vault_reward.vault().eq(&vault) { + vault_reward.increment_rewards(rewards)?; + return Ok(()); + } + } + + for vault_reward in self.vault_reward_routes.iter_mut() { + if vault_reward.vault().eq(&Pubkey::default()) { + *vault_reward = VaultRewardRoute::new(vault, rewards)?; + return Ok(()); + } + } + + Err(TipRouterError::OperatorRewardListFull) + } + + pub fn distribute_vault_reward_route( + &mut self, + vault: Pubkey, + rewards: u64, + ) -> Result<(), TipRouterError> { + if rewards == 0 { + return Ok(()); + } + + for route in self.vault_reward_routes.iter_mut() { + if route.vault().eq(&vault) { + route.decrement_rewards(rewards)?; + self.decrement_rewards_processed(rewards)?; + return Ok(()); + } + } + Err(TipRouterError::OperatorRewardNotFound) + } +} + +#[derive(Default, Debug, Clone, PartialEq, Eq, Copy, Zeroable, ShankType, Pod)] +#[repr(C)] +pub struct VaultRewardRoute { + vault: Pubkey, + rewards: PodU64, +} + +impl VaultRewardRoute { + pub fn new(vault: Pubkey, rewards: u64) -> Result { + Ok(Self { + vault, + rewards: PodU64::from(rewards), + }) + } + + pub const fn vault(&self) -> Pubkey { + self.vault + } + + pub fn rewards(&self) -> u64 { + self.rewards.into() + } + + fn set_rewards(&mut self, rewards: u64) -> Result<(), TipRouterError> { + self.rewards = PodU64::from(rewards); + Ok(()) + } + + pub fn increment_rewards(&mut self, rewards: u64) -> Result<(), TipRouterError> { + let current_rewards = self.rewards(); + + let new_rewards = current_rewards + .checked_add(rewards) + .ok_or(TipRouterError::ArithmeticOverflow)?; + + self.set_rewards(new_rewards) + } + + pub fn decrement_rewards(&mut self, rewards: u64) -> Result<(), TipRouterError> { + let current_rewards = self.rewards(); + + let new_rewards = current_rewards + .checked_sub(rewards) + .ok_or(TipRouterError::ArithmeticUnderflowError)?; + + self.set_rewards(new_rewards) + } } diff --git a/program/src/process_operator_epoch_reward_pool.rs b/program/src/process_operator_epoch_reward_pool.rs index 945244e..b5e7348 100644 --- a/program/src/process_operator_epoch_reward_pool.rs +++ b/program/src/process_operator_epoch_reward_pool.rs @@ -65,9 +65,9 @@ pub fn process_process_operator_epoch_reward_pool( let operator_epoch_reward_router_account = NcnRewardRouter::try_from_slice_unchecked_mut(&mut operator_epoch_reward_router_data)?; - operator_epoch_reward_router_account.process_incoming_rewards(account_balance)?; + operator_epoch_reward_router_account.route_incoming_rewards(account_balance)?; - operator_epoch_reward_router_account.process_reward_pool(&operator_snapshot)?; + operator_epoch_reward_router_account.route_reward_pool(&operator_snapshot)?; Ok(()) }