diff --git a/src/processor/mine.rs b/src/processor/mine.rs index 265bc61..b327e33 100644 --- a/src/processor/mine.rs +++ b/src/processor/mine.rs @@ -102,23 +102,23 @@ pub fn process_mine<'a, 'info>( sol_log(&format!("Base {}", reward)); // Apply staking multiplier. - // The multiplier can range 1x to 2x. To receive the maximum multiplier, the stake balance must be - // greater than or equal to two years worth of rewards at the selected difficulty. Miners are only - // eligable for a multipler if their last stake deposit was more than one minute ago. - if proof - .last_stake_at - .saturating_add(ONE_MINUTE) - .le(&clock.unix_timestamp) + // If user has greater than or equal to the max stake on the network, they will receive 2x multiplier. + // Any less than this, and they will receive between 1x and 2x. Miners are only eligable for a multipler + // if their last stake deposit was more than one minute ago. + if config.max_stake.gt(&0) + && proof + .last_stake_at + .saturating_add(ONE_MINUTE) + .le(&clock.unix_timestamp) { - let upper_bound = reward.saturating_mul(ONE_YEAR); let staking_reward = proof .balance - .min(upper_bound) + .min(config.max_stake) .saturating_mul(reward) - .saturating_div(upper_bound); + .saturating_div(config.max_stake); reward = reward.saturating_add(staking_reward); sol_log(&format!("Staking {}", staking_reward)); - }; + } // Apply spam penalty let t = clock.unix_timestamp; diff --git a/src/processor/stake.rs b/src/processor/stake.rs index 79be3dd..d988520 100644 --- a/src/processor/stake.rs +++ b/src/processor/stake.rs @@ -4,8 +4,11 @@ use solana_program::{ }; use crate::{ - instruction::StakeArgs, loaders::*, state::Proof, utils::AccountDeserialize, MINT_ADDRESS, - TREASURY_ADDRESS, + instruction::StakeArgs, + loaders::*, + state::{Config, Proof}, + utils::AccountDeserialize, + MINT_ADDRESS, TREASURY_ADDRESS, }; /// Stake deposits Ore into a miner's proof account to earn multiplier. Its responsibilies include: @@ -26,10 +29,13 @@ pub fn process_stake<'a, 'info>( let amount = u64::from_le_bytes(args.amount); // Load accounts - let [signer, proof_info, sender_info, treasury_tokens_info, token_program] = accounts else { + let [signer, config_info, proof_info, sender_info, treasury_tokens_info, token_program] = + accounts + else { return Err(ProgramError::NotEnoughAccountKeys); }; load_signer(signer)?; + load_config(config_info, true)?; load_proof(proof_info, signer.key, true)?; load_token_account(sender_info, Some(signer.key), &MINT_ADDRESS, true)?; load_token_account( @@ -49,6 +55,11 @@ pub fn process_stake<'a, 'info>( let clock = Clock::get().or(Err(ProgramError::InvalidAccountData))?; proof.last_stake_at = clock.unix_timestamp; + // Update the max stake tracker + let mut config_data = config_info.data.borrow_mut(); + let config = Config::try_from_bytes_mut(&mut config_data)?; + config.max_stake = config.max_stake.max(proof.balance); + // Distribute tokens from signer to treasury solana_program::program::invoke( &spl_token::instruction::transfer( diff --git a/src/state/bus.rs b/src/state/bus.rs index 36b3479..7275119 100644 --- a/src/state/bus.rs +++ b/src/state/bus.rs @@ -8,7 +8,6 @@ use crate::{ /// Bus accounts are responsible for distributing mining rewards. /// There are 8 busses total to minimize write-lock contention and allow for parallel mine operations. -/// Every epoch, the bus account rewards counters are topped up to 0.25 ORE each (2 ORE split amongst 8 busses). #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Pod, ShankAccount, Zeroable)] pub struct Bus { diff --git a/src/state/config.rs b/src/state/config.rs index 9c79cff..b3de137 100644 --- a/src/state/config.rs +++ b/src/state/config.rs @@ -17,8 +17,11 @@ pub struct Config { /// The base reward rate paid out for a hash of minimum difficulty. pub base_reward_rate: u64, - /// The timestamp of the last reset + /// The timestamp of the last reset. pub last_reset_at: i64, + + /// The largest stake account on the network. + pub max_stake: u64, } impl Discriminator for Config {