From 94a5d67dcf194c820627f91bc49be40e2a6e504e Mon Sep 17 00:00:00 2001 From: Hardhat Chad Date: Wed, 1 Oct 2025 12:05:13 -0700 Subject: [PATCH] checkpoint --- api/src/consts.rs | 6 +++++ program/src/checkpoint.rs | 39 ++++++++++++++++++++++++++++++- program/src/deploy.rs | 6 +++++ program/src/lib.rs | 48 ++++++++++++++++++++------------------- 4 files changed, 75 insertions(+), 24 deletions(-) diff --git a/api/src/consts.rs b/api/src/consts.rs index 81fa19e..a19b642 100644 --- a/api/src/consts.rs +++ b/api/src/consts.rs @@ -14,6 +14,9 @@ pub const ONE_ORE: u64 = 10u64.pow(TOKEN_DECIMALS as u32); /// The duration of one minute, in seconds. pub const ONE_MINUTE: i64 = 60; +/// The duration of one day, in seconds. +pub const ONE_DAY: i64 = 24 * 60 * 60; + /// The number of slots for breather between rounds. pub const INTERMISSION_SLOTS: u64 = 35; @@ -72,3 +75,6 @@ pub const DENOMINATOR_BPS: u64 = 10_000; /// The address of the boost reserve token account. pub const BOOST_RESERVE_TOKEN: Pubkey = pubkey!("Gce36ZUsBDJsoLrfCBxUB5Sfq2DsGunofStvxFx6rBiD"); + +/// The fee paid to bots if they checkpoint a user. +pub const CHECKPOINT_FEE: u64 = 10_000; // 0.00001 SOL diff --git a/program/src/checkpoint.rs b/program/src/checkpoint.rs index 5fabb40..da7d980 100644 --- a/program/src/checkpoint.rs +++ b/program/src/checkpoint.rs @@ -8,7 +8,8 @@ use steel::*; pub fn process_checkpoint(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult { // Load accounts. let clock = Clock::get()?; - let [signer_info, board_info, miner_info, round_info, treasury_info, system_program] = accounts + let [signer_info, automation_info, board_info, miner_info, round_info, treasury_info, system_program] = + accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; @@ -39,6 +40,32 @@ pub fn process_checkpoint(accounts: &[AccountInfo<'_>], _data: &[u8]) -> Program return Ok(()); } + // Calculate bot fee permissions. + let mut bot_fee = 0; + if clock.unix_timestamp > round.expires_at - ONE_DAY { + // We are in the last day before the round expires. + // Anyone is allowed to checkpoint and may collect the bot fee. + bot_fee = miner.checkpoint_fee; + miner.checkpoint_fee = 0; + } else { + // There is still time before the round expires. Bots may not yet checkpoint this account. + automation_info.has_seeds(&[AUTOMATION, &miner.authority.to_bytes()], &ore_api::ID)?; + if !automation_info.data_is_empty() { + let automation = automation_info + .as_account::(&ore_api::ID)? + .assert(|a| a.authority == miner.authority)?; + assert!( + *signer_info.key == miner.authority || *signer_info.key == automation.executor, + "Only the miner or automation executor can checkpoint this account" + ); + } else { + assert!( + *signer_info.key == miner.authority, + "Only the miner can checkpoint this account" + ); + } + } + // Calculate miner rewards. let mut rewards_sol = 0; let mut rewards_ore = 0; @@ -84,6 +111,9 @@ pub fn process_checkpoint(accounts: &[AccountInfo<'_>], _data: &[u8]) -> Program if rewards_sol > 0 { round_info.send(rewards_sol, &miner_info); } + if bot_fee > 0 { + miner_info.send(bot_fee, &signer_info); + } // Assert round has sufficient funds for rent. let account_size = 8 + std::mem::size_of::(); @@ -93,5 +123,12 @@ pub fn process_checkpoint(accounts: &[AccountInfo<'_>], _data: &[u8]) -> Program "Round does not have sufficient funds for rent" ); + let account_size = 8 + std::mem::size_of::(); + let required_rent = Rent::get()?.minimum_balance(account_size); + assert!( + miner_info.lamports() >= required_rent, + "Miner does not have sufficient funds for rent" + ); + Ok(()) } diff --git a/program/src/deploy.rs b/program/src/deploy.rs index a972b74..612f26e 100644 --- a/program/src/deploy.rs +++ b/program/src/deploy.rs @@ -165,6 +165,12 @@ pub fn process_deploy(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResul } } + // Pay checkpoint fee. + if miner.checkpoint_fee == 0 { + miner.checkpoint_fee = CHECKPOINT_FEE; + miner_info.collect(CHECKPOINT_FEE, &signer_info)?; + } + // Transfer SOL. if let Some(automation) = automation { automation.balance -= total_amount + automation.fee; diff --git a/program/src/lib.rs b/program/src/lib.rs index 61c556f..29d216e 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -44,33 +44,35 @@ pub fn process_instruction( accounts: &[AccountInfo], data: &[u8], ) -> ProgramResult { - // let (ix, data) = parse_instruction(&ore_api::ID, program_id, data)?; + let (ix, data) = parse_instruction(&ore_api::ID, program_id, data)?; - // match ix { - // // Miner - // OreInstruction::Automate => process_automate(accounts, data)?, - // OreInstruction::Boost => process_boost(accounts, data)?, - // OreInstruction::ClaimSOL => process_claim_sol(accounts, data)?, - // OreInstruction::ClaimORE => process_claim_ore(accounts, data)?, - // OreInstruction::Deploy => process_deploy(accounts, data)?, - // OreInstruction::Log => process_log(accounts, data)?, - // OreInstruction::Initialize => process_initialize(accounts, data)?, - // OreInstruction::Reset => process_reset(accounts, data)?, + match ix { + // Miner + OreInstruction::Automate => process_automate(accounts, data)?, + OreInstruction::Boost => process_boost(accounts, data)?, + OreInstruction::Checkpoint => process_checkpoint(accounts, data)?, + OreInstruction::ClaimSOL => process_claim_sol(accounts, data)?, + OreInstruction::ClaimORE => process_claim_ore(accounts, data)?, + OreInstruction::Deploy => process_deploy(accounts, data)?, + OreInstruction::Log => process_log(accounts, data)?, + // OreInstruction::Initialize => process_initialize(accounts, data)?, + OreInstruction::Reset => process_reset(accounts, data)?, - // // Staker - // OreInstruction::Deposit => process_deposit(accounts, data)?, - // OreInstruction::Withdraw => process_withdraw(accounts, data)?, - // OreInstruction::ClaimYield => process_claim_yield(accounts, data)?, + // Staker + OreInstruction::Deposit => process_deposit(accounts, data)?, + OreInstruction::Withdraw => process_withdraw(accounts, data)?, + OreInstruction::ClaimYield => process_claim_yield(accounts, data)?, - // // Admin - // OreInstruction::Bury => process_bury(accounts, data)?, - // OreInstruction::Wrap => process_wrap(accounts, data)?, - // OreInstruction::SetAdmin => process_set_admin(accounts, data)?, - // OreInstruction::SetFeeCollector => process_set_fee_collector(accounts, data)?, + // Admin + OreInstruction::Bury => process_bury(accounts, data)?, + OreInstruction::Wrap => process_wrap(accounts, data)?, + OreInstruction::SetAdmin => process_set_admin(accounts, data)?, + OreInstruction::SetFeeCollector => process_set_fee_collector(accounts, data)?, - // // Seeker - // OreInstruction::ClaimSeeker => process_claim_seeker(accounts, data)?, - // } + // Seeker + OreInstruction::ClaimSeeker => process_claim_seeker(accounts, data)?, + _ => return Err(ProgramError::InvalidInstructionData), + } Ok(()) }