diff --git a/api/src/sdk.rs b/api/src/sdk.rs index 0d7f8e2..611dd86 100644 --- a/api/src/sdk.rs +++ b/api/src/sdk.rs @@ -113,6 +113,27 @@ pub fn claim_ore(signer: Pubkey, amount: u64) -> Instruction { } } +pub fn redeem(signer: Pubkey, amount: u64) -> Instruction { + let mint_address = MINT_ADDRESS; + let sender_address = get_associated_token_address(&signer, &MINT_ADDRESS); + let treasury_address = TREASURY_ADDRESS; + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(mint_address, false), + AccountMeta::new(sender_address, false), + AccountMeta::new(treasury_address, false), + AccountMeta::new_readonly(system_program::ID, false), + AccountMeta::new_readonly(spl_token::ID, false), + ], + data: Redeem { + amount: amount.to_le_bytes(), + } + .to_bytes(), + } +} + // let [signer_info, board_info, mint_info, treasury_info, reserve_tokens_info, vault_info, system_program, token_program, slot_hashes_sysvar] = pub fn reset(signer: Pubkey, miners: Vec) -> Instruction { diff --git a/api/src/state/board.rs b/api/src/state/board.rs index e99e248..1a79f54 100644 --- a/api/src/state/board.rs +++ b/api/src/state/board.rs @@ -7,12 +7,12 @@ use super::OreAccount; #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct Board { - /// The commits for the round. - pub commits: [u64; 25], - /// The round number. pub id: u64, + /// The prospects for the round. + pub prospects: [u64; 25], + /// The timestamp at which the block starts mining. pub start_at: i64, @@ -25,8 +25,8 @@ pub struct Board { /// The hash of the end slot, provided by solana, used for random number generation. pub slot_hash: [u8; 32], - /// The top winner of the round. - pub top_winner: Pubkey, + /// The top miner of the round. + pub top_miner: Pubkey, /// The total amount of SOL prospected in the round. pub total_prospects: u64, diff --git a/api/src/state/miner.rs b/api/src/state/miner.rs index a01c127..f1ba87f 100644 --- a/api/src/state/miner.rs +++ b/api/src/state/miner.rs @@ -10,8 +10,8 @@ pub struct Miner { /// The authority of this miner account. pub authority: Pubkey, - /// The miner's committed square in the current round round. - pub commits: [u64; 25], + /// The miner's prospects in the current round. + pub prospects: [u64; 25], /// The amount of SOL this miner can claim. pub rewards_sol: u64, diff --git a/cli/src/main.rs b/cli/src/main.rs index 0acb5fd..34855e2 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -132,6 +132,17 @@ async fn claim_ore( Ok(()) } +async fn redeem( + rpc: &RpcClient, + payer: &solana_sdk::signer::keypair::Keypair, +) -> Result<(), anyhow::Error> { + let amount = std::env::var("AMOUNT").expect("Missing AMOUNT env var"); + let amount = u64::from_str(&amount).expect("Invalid AMOUNT"); + let ix = ore_api::sdk::redeem(payer.pubkey(), amount); + submit_transaction(rpc, payer, &[ix]).await?; + Ok(()) +} + async fn reset( rpc: &RpcClient, payer: &solana_sdk::signer::keypair::Keypair, @@ -244,7 +255,7 @@ async fn log_miner( println!("Miner"); println!(" address: {}", miner_address); println!(" authority: {}", authority); - println!(" commits: {:?}", miner.commits); + println!(" prospects: {:?}", miner.prospects); println!(" rewards_sol: {}", miner.rewards_sol); println!(" rewards_ore: {}", miner.rewards_ore); println!(" round_id: {}", miner.round_id); @@ -289,7 +300,7 @@ fn print_board(board: Board, clock: &Clock) { println!(" Slot hash: {:?}", board.slot_hash); println!(" Start slot: {}", board.start_slot); println!(" End slot: {}", board.end_slot); - println!(" Commits: {:?}", board.commits); + println!(" Prospects: {:?}", board.prospects); println!(" Total prospects: {}", board.total_prospects); println!(" Total vaulted: {}", board.total_vaulted); println!(" Total winnings: {}", board.total_winnings); diff --git a/program/src/claim_seeker.rs b/program/src/claim_seeker.rs index 1ec8f4a..f397da3 100644 --- a/program/src/claim_seeker.rs +++ b/program/src/claim_seeker.rs @@ -8,7 +8,7 @@ use spl_token_2022::{ pod::{PodCOption, PodMint}, }; -/// Claims ore for seeker device. +/// Claims ORE for seeker device. pub fn process_claim_seeker(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult { // Load accounts. let [signer_info, mint_info, _token_info] = accounts else { diff --git a/program/src/initialize.rs b/program/src/initialize.rs index df1c9a3..137f032 100644 --- a/program/src/initialize.rs +++ b/program/src/initialize.rs @@ -29,13 +29,13 @@ pub fn process_initialize(accounts: &[AccountInfo<'_>], _data: &[u8]) -> Program &[BOARD], )?; let board = board_info.as_account_mut::(&ore_api::ID)?; - board.commits = [0; 25]; + board.prospects = [0; 25]; board.id = 0; board.start_at = 0; board.start_slot = 0; board.end_slot = 0; board.slot_hash = [0; 32]; - board.top_winner = Pubkey::default(); + board.top_miner = Pubkey::default(); board.total_prospects = 0; board.total_vaulted = 0; board.total_winnings = 0; diff --git a/program/src/prospect.rs b/program/src/prospect.rs index 7e5c16d..dc67c99 100644 --- a/program/src/prospect.rs +++ b/program/src/prospect.rs @@ -43,7 +43,7 @@ pub fn process_prospect(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramRes )?; let miner = miner_info.as_account_mut::(&ore_api::ID)?; miner.authority = *signer_info.key; - miner.commits = [0; 25]; + miner.prospects = [0; 25]; miner.rewards_sol = 0; miner.rewards_ore = 0; miner.round_id = board.id; @@ -58,7 +58,7 @@ pub fn process_prospect(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramRes // Reset board. if board.slot_hash != [0; 32] { - board.commits = [0; 25]; + board.prospects = [0; 25]; board.id += 1; board.slot_hash = [0; 32]; board.start_slot = clock.slot; @@ -70,7 +70,7 @@ pub fn process_prospect(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramRes // Reset miner if miner.round_id != board.id { - miner.commits = [0; 25]; + miner.prospects = [0; 25]; miner.round_id = board.id; } @@ -86,17 +86,17 @@ pub fn process_prospect(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramRes let amount = amount - fee; // Update miner - let is_first_play_on_square = miner.commits[square_id as usize] == 0; - miner.commits[square_id as usize] += amount; + let is_first_move = miner.prospects[square_id as usize] == 0; + miner.prospects[square_id as usize] += amount; // Update square - if is_first_play_on_square { + if is_first_move { square.miners[square.count as usize] = *signer_info.key; square.count += 1; } // Update board - board.commits[square_id as usize] += amount; + board.prospects[square_id as usize] += amount; board.total_prospects += amount; // Transfer prospects. diff --git a/program/src/redeem.rs b/program/src/redeem.rs index ac35a02..188d502 100644 --- a/program/src/redeem.rs +++ b/program/src/redeem.rs @@ -16,9 +16,7 @@ pub fn process_redeem(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResul signer_info.is_signer()?; let mint = mint_info.has_address(&MINT_ADDRESS)?.as_mint()?; let sender = sender_info.as_associated_token_account(&signer_info.key, &mint_info.key)?; - let treasury = treasury_info - .as_account_mut::(&ore_api::ID)? - .assert_mut(|t| t.balance >= amount)?; + let treasury = treasury_info.as_account_mut::(&ore_api::ID)?; system_program.is_program(&system_program::ID)?; token_program.is_program(&spl_token::ID)?; @@ -32,6 +30,10 @@ pub fn process_redeem(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResul burn(sender_info, mint_info, signer_info, token_program, amount)?; // Transfer SOL to recipient. + assert!( + treasury.balance >= redemption_amount, + "Redemption too large" + ); treasury_info.send(redemption_amount, signer_info); treasury.balance -= redemption_amount; diff --git a/program/src/reset.rs b/program/src/reset.rs index c0ebee7..2b6a1ee 100644 --- a/program/src/reset.rs +++ b/program/src/reset.rs @@ -1,5 +1,5 @@ use ore_api::prelude::*; -use solana_program::{log::sol_log, slot_hashes::SlotHashes}; +use solana_program::slot_hashes::SlotHashes; use steel::*; /// Claims a block reward. @@ -28,9 +28,7 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul token_program.is_program(&spl_token::ID)?; slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?; - sol_log("A"); - - // Mint tokens to the boost reserve. + // Mint tokens to the boost program. mint_to_signed( mint_info, reserve_tokens_info, @@ -40,102 +38,78 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul &[TREASURY], )?; - sol_log("B"); - - // Sample slot hashes. - let (winning_square, square_commits) = + // Sample slot hash. + let (winning_square, square_prospects) = if let Ok(slot_hash) = get_slot_hash(board.end_slot, slot_hashes_sysvar) { board.slot_hash = slot_hash; let winning_square = get_winning_square(&slot_hash); - let square_commits = board.commits[winning_square as usize]; - (winning_square, square_commits) + let square_prospects = board.prospects[winning_square as usize]; + (winning_square, square_prospects) } else { // Cannot get slot hash. No one wins. board.slot_hash = [u8::MAX; 32]; (u64::MAX, 0) }; - sol_log("C"); - // No one won. Vault all prospects. - if square_commits == 0 { + if square_prospects == 0 { board.total_vaulted = board.total_prospects; treasury.balance += board.total_prospects; board_info.send(board.total_prospects, &treasury_info); return Ok(()); } - sol_log("D"); - // Get winnings amount (prospects on all non-winning squares). let mut winnings = 0; - for (i, commits) in board.commits.iter().enumerate() { + for (i, prospects) in board.prospects.iter().enumerate() { if i as u64 != winning_square { - winnings += commits; + winnings += prospects; } } - sol_log("E"); - - // Get vault amount. - let vault_amount = winnings / 10; // Vault 10% of winnings. - board.total_vaulted = vault_amount; + // Get vault amount (backing ORE floor price with SOL). + let vault_amount = winnings / 10; // 10% of winnings. let winnings = winnings - vault_amount; - // board_info.send(vault_amount, &treasury_info); + board.total_vaulted = vault_amount; treasury.balance += vault_amount; - sol_log("F"); - - // Payout winnings to miners. - let mut top_winner = None; - let mut top_winner_commits = 0; + // Record miner rewards. + let mut top_miner = None; + let mut top_miner_prospects = 0; let mut rewards_sol = [0; 16]; let mut checksum = 0; for (i, miner_info) in miner_accounts.iter().enumerate() { - sol_log("G"); - // Transfer winnings to miner. let miner = miner_info .as_account_mut::(&ore_api::ID)? .assert_mut(|m| m.round_id == board.id)?; - let miner_commits = miner.commits[winning_square as usize]; - let rewards = miner_commits + (winnings * miner_commits / square_commits); // Winners get their own prospect back plus their share of the winnings. + let miner_prospects = miner.prospects[winning_square as usize]; + let rewards = miner_prospects + (winnings * miner_prospects / square_prospects); // Winners get their own prospect back plus their share of the winnings. + checksum += miner_prospects; miner.rewards_sol += rewards; miner.lifetime_rewards_sol += rewards; - checksum += miner_commits; rewards_sol[i] = rewards; - // board_info.send(rewards, &miner_info); // Find the top winner. - if miner_commits > top_winner_commits { - sol_log("H"); - top_winner_commits = miner_commits; - top_winner = Some(i); - // top_winner = Some(miner); + if miner_prospects > top_miner_prospects { + top_miner_prospects = miner_prospects; + top_miner = Some(i); } } - sol_log("I"); - // Verify checksum. - if checksum != square_commits { + if checksum != square_prospects { // This can only happen if the caller didn't provide full set of winning miners. - sol_log("J"); return Err(ProgramError::InvalidAccountData); } - sol_log("K"); - - // Payout reward to top winner. - if let Some(i) = top_winner { - sol_log("L"); + // Payout reward to top miner. + if let Some(i) = top_miner { let miner = miner_accounts[i].as_account_mut::(&ore_api::ID)?; let mint_amount = ONE_ORE.min(MAX_SUPPLY - mint.supply()); if mint_amount > 0 { - // sol_log("M"); miner.rewards_ore += mint_amount; miner.lifetime_rewards_ore += mint_amount; - board.top_winner = miner.authority; - // sol_log("M2"); + board.top_miner = miner.authority; mint_to_signed( mint_info, treasury_tokens_info, @@ -144,38 +118,9 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul mint_amount, &[TREASURY], )?; - // sol_log("N"); } - - // miner.rewards_ore += rewards_sol[i]; - // miner.lifetime_rewards_ore += rewards_sol[i]; - // board.top_winner = miner.authority; } - // if let Some(miner) = top_winner { - // sol_log("L"); - // let mint_amount = ONE_ORE.min(MAX_SUPPLY - mint.supply()); - // sol_log(&format!("mint_amount: {}", mint_amount)); - // if mint_amount > 0 { - // sol_log("M"); - // miner.rewards_ore += mint_amount; - // miner.lifetime_rewards_ore += mint_amount; - // board.top_winner = miner.authority; - // sol_log("M2"); - // mint_to_signed( - // mint_info, - // treasury_tokens_info, - // treasury_info, - // token_program, - // mint_amount, - // &[TREASURY], - // )?; - // sol_log("N"); - // } - // } - - sol_log("O"); - // Update board. board.total_winnings = winnings; @@ -185,8 +130,6 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul board_info.send(rewards_sol[i], &miner_info); } - // Send vault amount to treasury. - Ok(()) }