From c35d60ff4d4fbe2c2fa8373366f53a2a370d7e5a Mon Sep 17 00:00:00 2001 From: Hardhat Chad Date: Thu, 12 Jun 2025 20:57:28 -0700 Subject: [PATCH] motherlode --- api/src/consts.rs | 8 +++++- api/src/state/block.rs | 13 +++++---- program/src/close.rs | 7 +++-- program/src/mine.rs | 27 ++++++++++++++----- program/src/open.rs | 61 +++++++++++++++++++++++++++++++----------- 5 files changed, 84 insertions(+), 32 deletions(-) diff --git a/api/src/consts.rs b/api/src/consts.rs index 468fd42..985171f 100644 --- a/api/src/consts.rs +++ b/api/src/consts.rs @@ -71,7 +71,13 @@ pub const HASH_TOKEN_SUPPLY: u64 = 10_000_000; pub const VIRTUAL_LIQUIDITY: u64 = ONE_ORE * 5; /// The minimum difficulty required for payout. -pub const MIN_DIFFICULTY: u64 = 10; +pub const NUGGET_DIFFICULTY: u64 = 10; + +/// The difficulty threshold for the motherlode payout. +pub const MOTHERLOAD_DIFFICULTY: u64 = 35; + +/// The fee to open a block. +pub const OPEN_FEE: u64 = ONE_ORE / 100; // The reward rate per satisfying hash (0.002048 ORE). // pub const REWARD_RATE: u64 = 204_800_000; diff --git a/api/src/state/block.rs b/api/src/state/block.rs index 6542acb..ab12675 100644 --- a/api/src/state/block.rs +++ b/api/src/state/block.rs @@ -44,19 +44,22 @@ pub struct Block { #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct RewardConfig { /// The reward paid to the submitter of the best hash. - pub best_hash_reward: u64, + pub lode_reward: u64, /// The authority of the miner who submitted the best hash. - pub best_hash_authority: Pubkey, + pub lode_authority: Pubkey, /// The best hash. - pub best_hash: [u8; 32], + pub lode_hash: [u8; 32], + + /// The threshold difficulty for the motherlode payout. + pub motherlode_threshold: u64, /// The reward rate paid to hashes satisfying the difficulty threshold. - pub difficulty_reward: u64, + pub nugget_reward: u64, /// The minimum difficulty required for payout. - pub difficulty_threshold: u64, + pub nugget_threshold: u64, } impl Block { diff --git a/program/src/close.rs b/program/src/close.rs index 167c6fc..898a0b9 100644 --- a/program/src/close.rs +++ b/program/src/close.rs @@ -30,15 +30,14 @@ pub fn process_close(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul token_program.is_program(&spl_token::ID)?; // Payout block reward. - if block.reward.best_hash_reward > 0 && block.reward.best_hash_authority != Pubkey::default() { - recipient_info - .as_associated_token_account(&block.reward.best_hash_authority, &MINT_ADDRESS)?; + if block.reward.lode_reward > 0 && block.reward.lode_authority != Pubkey::default() { + recipient_info.as_associated_token_account(&block.reward.lode_authority, &MINT_ADDRESS)?; mint_to_signed( mint_quote_info, recipient_info, treasury_info, token_program, - block.reward.best_hash_reward, + block.reward.lode_reward, &[TREASURY], )?; } diff --git a/program/src/mine.rs b/program/src/mine.rs index eddaf33..5ca27c9 100644 --- a/program/src/mine.rs +++ b/program/src/mine.rs @@ -11,7 +11,7 @@ pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult // Load accounts. let clock = Clock::get()?; - let [signer_info, authority_info, block_info, commitment_info, market_info, miner_info, mint_hash_info, mint_ore_info, permit_info, recipient_info, treasury_info, system_program, token_program, slot_hashes_sysvar] = + let [signer_info, authority_info, block_info, commitment_info, market_info, miner_info, mint_hash_info, mint_ore_info, permit_info, recipient_info, treasury_info, treasury_tokens_info, system_program, token_program, slot_hashes_sysvar] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); @@ -42,6 +42,9 @@ pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult .is_writable()? .as_associated_token_account(&miner.authority, &MINT_ADDRESS)?; treasury_info.has_address(&TREASURY_ADDRESS)?; + let treasury_tokens = treasury_tokens_info + .is_writable()? + .as_associated_token_account(&treasury_info.key, &mint_ore_info.key)?; system_program.is_program(&system_program::ID)?; token_program.is_program(&spl_token::ID)?; slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?; @@ -104,16 +107,28 @@ pub fn process_mine(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult // Score and increment rewards. let score = difficulty(miner.hash) as u64; - if score >= block.reward.difficulty_threshold { + if score >= block.reward.nugget_threshold { block.winning_hashes += 1; miner.winning_hashes += 1; - miner_reward += block.reward.difficulty_reward; + miner_reward += block.reward.nugget_reward; } // If hash is best hash, update best hash. - if miner.hash < block.reward.best_hash { - block.reward.best_hash = miner.hash; - block.reward.best_hash_authority = miner.authority; + if miner.hash < block.reward.lode_hash { + block.reward.lode_hash = miner.hash; + block.reward.lode_authority = miner.authority; + } + + // If hash is motherlode hash, pay the motherlode reward. + if score >= block.reward.motherlode_threshold { + transfer_signed( + authority_info, + treasury_tokens_info, + recipient_info, + token_program, + treasury_tokens.amount(), + &[TREASURY], + )?; } } diff --git a/program/src/open.rs b/program/src/open.rs index 89f31d0..639ce83 100644 --- a/program/src/open.rs +++ b/program/src/open.rs @@ -12,7 +12,7 @@ pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult // Load accounts. let clock = Clock::get()?; - let [signer_info, block_info, collateral_info, commitment_info, market_info, mint_base_info, mint_quote_info, vault_base_info, vault_quote_info, system_program, token_program, associated_token_program, rent_sysvar] = + let [signer_info, block_info, collateral_info, commitment_info, market_info, mint_base_info, mint_quote_info, sender_info, treasury_info, treasury_tokens_info, vault_base_info, vault_quote_info, system_program, token_program, associated_token_program, rent_sysvar] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); @@ -31,11 +31,40 @@ pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult .is_writable()? .has_seeds(&[MINT, &id.to_le_bytes()], &ore_api::ID)?; mint_quote_info.has_address(&MINT_ADDRESS)?.as_mint()?; + sender_info + .is_writable()? + .as_associated_token_account(&signer_info.key, &mint_quote_info.key)? + .assert_mut(|t| t.amount() >= OPEN_FEE)?; + treasury_info.has_address(&TREASURY_ADDRESS)?; system_program.is_program(&system_program::ID)?; token_program.is_program(&spl_token::ID)?; associated_token_program.is_program(&spl_associated_token_account::ID)?; rent_sysvar.is_sysvar(&sysvar::rent::ID)?; + // Load treasury token accounts. + if treasury_tokens_info.data_is_empty() { + create_associated_token_account( + signer_info, + treasury_info, + treasury_tokens_info, + mint_quote_info, + system_program, + token_program, + associated_token_program, + )?; + } else { + treasury_tokens_info.as_associated_token_account(&TREASURY_ADDRESS, mint_quote_info.key)?; + } + + // Pay block opening fee. + transfer( + signer_info, + sender_info, + treasury_tokens_info, + token_program, + OPEN_FEE, + )?; + // Error out if start slot is within the current period. let start_slot = id * 1500; let current_period_start = (clock.slot / 1500) * 1500; @@ -55,11 +84,12 @@ pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult let block = block_info.as_account_mut::(&ore_api::ID)?; block.id = id; block.reward = RewardConfig { - best_hash: [0; 32], - best_hash_authority: Pubkey::default(), - best_hash_reward: 0, - difficulty_threshold: MIN_DIFFICULTY, - difficulty_reward: 0, + lode_hash: [0; 32], + lode_authority: Pubkey::default(), + lode_reward: 0, + motherlode_threshold: MOTHERLOAD_DIFFICULTY, + nugget_reward: 0, + nugget_threshold: NUGGET_DIFFICULTY, }; block.slot_hash = [0; 32]; block.start_slot = start_slot; @@ -69,13 +99,13 @@ pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult // Select reward strategy. let noise_seed = block.id.to_le_bytes(); let noise = hash(&noise_seed); - let best_hash_reward = ONE_ORE * generate_best_hash_reward(noise) as u64; + let lode_reward = ONE_ORE * generate_best_hash_reward(noise) as u64; let target_block_reward = ONE_ORE * 10; let expected_hashes_per_block = HASH_TOKEN_SUPPLY / 2; - let expected_qualifying_hashes = expected_hashes_per_block / 2u64.pow(MIN_DIFFICULTY as u32); - let difficulty_reward = (target_block_reward - best_hash_reward) / expected_qualifying_hashes; - block.reward.best_hash_reward = best_hash_reward; - block.reward.difficulty_reward = difficulty_reward; + let expected_qualifying_hashes = expected_hashes_per_block / 2u64.pow(NUGGET_DIFFICULTY as u32); + let difficulty_reward = (target_block_reward - lode_reward) / expected_qualifying_hashes; + block.reward.lode_reward = lode_reward; + block.reward.nugget_reward = difficulty_reward; // Initialize market. create_program_account::( @@ -232,14 +262,13 @@ fn test_hash_rewards() { for i in 0u64..1000 { let noise_seed = i.to_le_bytes(); let noise = hash(&noise_seed); - let best_hash_reward = ONE_ORE * generate_best_hash_reward(noise) as u64; + let lode_reward = ONE_ORE * generate_best_hash_reward(noise) as u64; let target_block_reward = ONE_ORE * 10; let expected_hashes_per_block = HASH_TOKEN_SUPPLY / 2; let expected_qualifying_hashes = - expected_hashes_per_block / 2u64.pow(MIN_DIFFICULTY as u32); - let difficulty_reward = - (target_block_reward - best_hash_reward) / expected_qualifying_hashes; - println!("{}: {} {}", i, best_hash_reward, difficulty_reward); + expected_hashes_per_block / 2u64.pow(NUGGET_DIFFICULTY as u32); + let difficulty_reward = (target_block_reward - lode_reward) / expected_qualifying_hashes; + println!("{}: {} {}", i, lode_reward, difficulty_reward); } // assert!(false); }