mirror of
https://github.com/d0zingcat/ore.git
synced 2026-05-13 15:09:57 +00:00
close
This commit is contained in:
@@ -14,8 +14,26 @@ 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 hour, in seconds.
|
||||
pub const ONE_HOUR: i64 = 60 * ONE_MINUTE;
|
||||
|
||||
/// The duration of one day, in seconds.
|
||||
pub const ONE_DAY: i64 = 24 * 60 * 60;
|
||||
pub const ONE_DAY: i64 = 24 * ONE_HOUR;
|
||||
|
||||
/// The number of seconds for when the winning square expires.
|
||||
pub const ONE_WEEK: i64 = 7 * ONE_DAY;
|
||||
|
||||
/// The number of slots in one week.
|
||||
pub const ONE_MINUTE_SLOTS: u64 = 150;
|
||||
|
||||
/// The number of slots in one hour.
|
||||
pub const ONE_HOUR_SLOTS: u64 = 60 * ONE_MINUTE_SLOTS;
|
||||
|
||||
/// The number of slots in one day.
|
||||
pub const ONE_DAY_SLOTS: u64 = 24 * ONE_HOUR_SLOTS;
|
||||
|
||||
/// The number of slots in one week.
|
||||
pub const ONE_WEEK_SLOTS: u64 = 7 * ONE_DAY_SLOTS;
|
||||
|
||||
/// The number of slots for breather between rounds.
|
||||
pub const INTERMISSION_SLOTS: u64 = 35;
|
||||
|
||||
@@ -9,24 +9,25 @@ pub enum OreInstruction {
|
||||
Checkpoint = 2,
|
||||
ClaimSOL = 3,
|
||||
ClaimORE = 4,
|
||||
Deploy = 5,
|
||||
Initialize = 6,
|
||||
Log = 7,
|
||||
Reset = 8,
|
||||
Close = 5,
|
||||
Deploy = 6,
|
||||
Initialize = 7,
|
||||
Log = 8,
|
||||
Reset = 9,
|
||||
|
||||
// Staker
|
||||
Deposit = 9,
|
||||
Withdraw = 10,
|
||||
ClaimYield = 11,
|
||||
Deposit = 10,
|
||||
Withdraw = 11,
|
||||
ClaimYield = 12,
|
||||
|
||||
// Admin
|
||||
Bury = 12,
|
||||
Wrap = 13,
|
||||
SetAdmin = 14,
|
||||
SetFeeCollector = 15,
|
||||
Bury = 13,
|
||||
Wrap = 14,
|
||||
SetAdmin = 15,
|
||||
SetFeeCollector = 16,
|
||||
|
||||
// Seeker
|
||||
ClaimSeeker = 16,
|
||||
ClaimSeeker = 17,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@@ -149,8 +150,13 @@ pub struct ClaimSeeker {}
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
pub struct Checkpoint {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
pub struct Close {}
|
||||
|
||||
instruction!(OreInstruction, Automate);
|
||||
instruction!(OreInstruction, Boost);
|
||||
instruction!(OreInstruction, Close);
|
||||
instruction!(OreInstruction, Checkpoint);
|
||||
instruction!(OreInstruction, ClaimSOL);
|
||||
instruction!(OreInstruction, ClaimORE);
|
||||
|
||||
@@ -10,9 +10,6 @@ pub struct Board {
|
||||
/// The current round number.
|
||||
pub round_id: u64,
|
||||
|
||||
/// The timestamp at which the current round starts mining.
|
||||
pub start_at: i64,
|
||||
|
||||
/// The slot at which the current round starts mining.
|
||||
pub start_slot: u64,
|
||||
|
||||
@@ -20,82 +17,6 @@ pub struct Board {
|
||||
pub end_slot: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
|
||||
pub struct Round {
|
||||
/// The round number.
|
||||
pub id: u64,
|
||||
|
||||
/// The amount of SOL deployed in each square.
|
||||
pub deployed: [u64; 25],
|
||||
|
||||
/// The hash of the end slot, provided by solana, used for random number generation.
|
||||
pub slot_hash: [u8; 32],
|
||||
|
||||
/// The slot at which claims for this round account end.
|
||||
pub expires_at: i64,
|
||||
|
||||
/// The amount of ORE in the motherlode.
|
||||
pub motherlode: u64,
|
||||
|
||||
/// The account to which rent should be returned when this account is closed.
|
||||
pub rent_payer: Pubkey,
|
||||
|
||||
/// The top miner of the round.
|
||||
pub top_miner: Pubkey,
|
||||
|
||||
/// The amount of ORE to distribute to the top miner.
|
||||
pub top_miner_reward: u64,
|
||||
|
||||
/// The total amount of SOL deployed in the round.
|
||||
pub total_deployed: u64,
|
||||
|
||||
/// The total amount of SOL put in the ORE vault.
|
||||
pub total_vaulted: u64,
|
||||
|
||||
/// The total amount of SOL won by miners for the round.
|
||||
pub total_winnings: u64,
|
||||
}
|
||||
|
||||
impl Round {
|
||||
pub fn rng(&self) -> u64 {
|
||||
if self.slot_hash == [0; 32] {
|
||||
return 0;
|
||||
}
|
||||
let r1 = u64::from_le_bytes(self.slot_hash[0..8].try_into().unwrap());
|
||||
let r2 = u64::from_le_bytes(self.slot_hash[8..16].try_into().unwrap());
|
||||
let r3 = u64::from_le_bytes(self.slot_hash[16..24].try_into().unwrap());
|
||||
let r4 = u64::from_le_bytes(self.slot_hash[24..32].try_into().unwrap());
|
||||
let r = r1 ^ r2 ^ r3 ^ r4;
|
||||
r
|
||||
}
|
||||
|
||||
pub fn winning_square(&self, rng: u64) -> usize {
|
||||
(rng % 25) as usize
|
||||
}
|
||||
|
||||
pub fn top_miner_sample(&self, rng: u64, winning_square: usize) -> u64 {
|
||||
if self.deployed[winning_square] == 0 {
|
||||
return 0;
|
||||
}
|
||||
rng.reverse_bits() % self.deployed[winning_square]
|
||||
}
|
||||
|
||||
pub fn calculate_total_winnings(&self, winning_square: usize) -> u64 {
|
||||
let mut total_winnings = 0;
|
||||
for (i, &deployed) in self.deployed.iter().enumerate() {
|
||||
if i != winning_square {
|
||||
total_winnings += deployed;
|
||||
}
|
||||
}
|
||||
total_winnings
|
||||
}
|
||||
|
||||
pub fn did_hit_motherlode(&self, rng: u64) -> bool {
|
||||
rng.reverse_bits() % 625 == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Board {
|
||||
pub fn pda(&self) -> (Pubkey, u8) {
|
||||
board_pda()
|
||||
@@ -103,20 +24,3 @@ impl Board {
|
||||
}
|
||||
|
||||
account!(OreAccount, Board);
|
||||
|
||||
account!(OreAccount, Round);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use solana_program::rent::Rent;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_rent() {
|
||||
let size_of_round = 8 + std::mem::size_of::<Round>();
|
||||
let required_rent = Rent::default().minimum_balance(size_of_round);
|
||||
println!("required_rent: {}", required_rent);
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ mod automation;
|
||||
mod board;
|
||||
mod config;
|
||||
mod miner;
|
||||
mod round;
|
||||
mod seeker;
|
||||
// mod square;
|
||||
mod stake;
|
||||
mod treasury;
|
||||
|
||||
@@ -11,8 +11,8 @@ pub use automation::*;
|
||||
pub use board::*;
|
||||
pub use config::*;
|
||||
pub use miner::*;
|
||||
pub use round::*;
|
||||
pub use seeker::*;
|
||||
// pub use square::*;
|
||||
pub use stake::*;
|
||||
pub use treasury::*;
|
||||
|
||||
@@ -30,7 +30,6 @@ pub enum OreAccount {
|
||||
|
||||
//
|
||||
Board = 105,
|
||||
// Square = 106,
|
||||
Seeker = 107,
|
||||
Stake = 108,
|
||||
Round = 109,
|
||||
@@ -56,10 +55,6 @@ pub fn seeker_pda(mint: Pubkey) -> (Pubkey, u8) {
|
||||
Pubkey::find_program_address(&[SEEKER, &mint.to_bytes()], &crate::ID)
|
||||
}
|
||||
|
||||
// pub fn square_pda() -> (Pubkey, u8) {
|
||||
// Pubkey::find_program_address(&[SQUARE], &crate::ID)
|
||||
// }
|
||||
|
||||
pub fn round_pda(id: u64) -> (Pubkey, u8) {
|
||||
Pubkey::find_program_address(&[ROUND, &id.to_le_bytes()], &crate::ID)
|
||||
}
|
||||
|
||||
102
api/src/state/round.rs
Normal file
102
api/src/state/round.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use steel::*;
|
||||
|
||||
use crate::state::round_pda;
|
||||
|
||||
use super::OreAccount;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
|
||||
pub struct Round {
|
||||
/// The round number.
|
||||
pub id: u64,
|
||||
|
||||
/// The amount of SOL deployed in each square.
|
||||
pub deployed: [u64; 25],
|
||||
|
||||
/// The hash of the end slot, provided by solana, used for random number generation.
|
||||
pub slot_hash: [u8; 32],
|
||||
|
||||
/// The slot at which claims for this round account end.
|
||||
pub expires_at: u64,
|
||||
|
||||
/// The amount of ORE in the motherlode.
|
||||
pub motherlode: u64,
|
||||
|
||||
/// The account to which rent should be returned when this account is closed.
|
||||
pub rent_payer: Pubkey,
|
||||
|
||||
/// The top miner of the round.
|
||||
pub top_miner: Pubkey,
|
||||
|
||||
/// The amount of ORE to distribute to the top miner.
|
||||
pub top_miner_reward: u64,
|
||||
|
||||
/// The total amount of SOL deployed in the round.
|
||||
pub total_deployed: u64,
|
||||
|
||||
/// The total amount of SOL put in the ORE vault.
|
||||
pub total_vaulted: u64,
|
||||
|
||||
/// The total amount of SOL won by miners for the round.
|
||||
pub total_winnings: u64,
|
||||
}
|
||||
|
||||
impl Round {
|
||||
pub fn pda(&self) -> (Pubkey, u8) {
|
||||
round_pda(self.id)
|
||||
}
|
||||
|
||||
pub fn rng(&self) -> u64 {
|
||||
if self.slot_hash == [0; 32] {
|
||||
return 0;
|
||||
}
|
||||
let r1 = u64::from_le_bytes(self.slot_hash[0..8].try_into().unwrap());
|
||||
let r2 = u64::from_le_bytes(self.slot_hash[8..16].try_into().unwrap());
|
||||
let r3 = u64::from_le_bytes(self.slot_hash[16..24].try_into().unwrap());
|
||||
let r4 = u64::from_le_bytes(self.slot_hash[24..32].try_into().unwrap());
|
||||
let r = r1 ^ r2 ^ r3 ^ r4;
|
||||
r
|
||||
}
|
||||
|
||||
pub fn winning_square(&self, rng: u64) -> usize {
|
||||
(rng % 25) as usize
|
||||
}
|
||||
|
||||
pub fn top_miner_sample(&self, rng: u64, winning_square: usize) -> u64 {
|
||||
if self.deployed[winning_square] == 0 {
|
||||
return 0;
|
||||
}
|
||||
rng.reverse_bits() % self.deployed[winning_square]
|
||||
}
|
||||
|
||||
pub fn calculate_total_winnings(&self, winning_square: usize) -> u64 {
|
||||
let mut total_winnings = 0;
|
||||
for (i, &deployed) in self.deployed.iter().enumerate() {
|
||||
if i != winning_square {
|
||||
total_winnings += deployed;
|
||||
}
|
||||
}
|
||||
total_winnings
|
||||
}
|
||||
|
||||
pub fn did_hit_motherlode(&self, rng: u64) -> bool {
|
||||
rng.reverse_bits() % 625 == 0
|
||||
}
|
||||
}
|
||||
|
||||
account!(OreAccount, Round);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use solana_program::rent::Rent;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_rent() {
|
||||
let size_of_round = 8 + std::mem::size_of::<Round>();
|
||||
let required_rent = Rent::default().minimum_balance(size_of_round);
|
||||
println!("required_rent: {}", required_rent);
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
use steel::*;
|
||||
|
||||
use crate::state::square_pda;
|
||||
|
||||
use super::OreAccount;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
|
||||
pub struct Square {
|
||||
/// The count of miners on each square.
|
||||
pub count: [u64; 25],
|
||||
|
||||
/// The deployments of all players on each square.
|
||||
pub deployed: [[u64; 16]; 25],
|
||||
|
||||
/// The miners authorities on each square.
|
||||
pub miners: [[Pubkey; 16]; 25],
|
||||
}
|
||||
|
||||
impl Square {
|
||||
pub fn pda() -> (Pubkey, u8) {
|
||||
square_pda()
|
||||
}
|
||||
}
|
||||
|
||||
account!(OreAccount, Square);
|
||||
@@ -34,7 +34,7 @@ pub fn process_checkpoint(accounts: &[AccountInfo<'_>], _data: &[u8]) -> Program
|
||||
system_program.is_program(&system_program::ID)?;
|
||||
|
||||
// Ensure round is not expired.
|
||||
if clock.unix_timestamp >= round.expires_at {
|
||||
if clock.slot >= round.expires_at {
|
||||
// In this case, the miner forfeits any potential rewards and their checkpoint is recorded.
|
||||
miner.checkpoint_id = round.id;
|
||||
return Ok(());
|
||||
@@ -42,13 +42,14 @@ pub fn process_checkpoint(accounts: &[AccountInfo<'_>], _data: &[u8]) -> Program
|
||||
|
||||
// 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.
|
||||
if clock.slot >= round.expires_at - ONE_DAY_SLOTS {
|
||||
// The round expires in less than 24h.
|
||||
// Anyone is allowed to checkpoint this account 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.
|
||||
// There is still time remaining 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
|
||||
|
||||
23
program/src/close.rs
Normal file
23
program/src/close.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use ore_api::prelude::*;
|
||||
use steel::*;
|
||||
|
||||
/// Closes a round accound, and returns the rent to the rent payer.
|
||||
pub fn process_close(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
||||
// Load accounts.
|
||||
let clock = Clock::get()?;
|
||||
let [signer_info, rent_payer_info, round_info, system_program] = accounts else {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
};
|
||||
signer_info.is_signer()?;
|
||||
rent_payer_info.is_writable()?;
|
||||
round_info
|
||||
.as_account_mut::<Round>(&ore_api::ID)?
|
||||
.assert_mut(|r| r.expires_at >= clock.slot)? // Ensure round has ended.
|
||||
.assert_mut(|r| r.rent_payer == *rent_payer_info.key)?; // Ensure the rent payer is the correct one.
|
||||
system_program.is_program(&system_program::ID)?;
|
||||
|
||||
// Close the account.
|
||||
round_info.close(rent_payer_info)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -11,7 +11,7 @@ pub fn process_deploy(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResul
|
||||
|
||||
// Load accounts.
|
||||
let clock = Clock::get()?;
|
||||
let [signer_info, authority_info, automation_info, board_info, miner_info, round_info, round_prev_info, system_program] =
|
||||
let [signer_info, authority_info, automation_info, board_info, miner_info, round_info, system_program] =
|
||||
accounts
|
||||
else {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
|
||||
@@ -6,6 +6,7 @@ mod claim_ore;
|
||||
mod claim_seeker;
|
||||
mod claim_sol;
|
||||
mod claim_yield;
|
||||
mod close;
|
||||
mod deploy;
|
||||
mod deposit;
|
||||
// mod initialize;
|
||||
@@ -25,6 +26,7 @@ use claim_ore::*;
|
||||
use claim_seeker::*;
|
||||
use claim_sol::*;
|
||||
use claim_yield::*;
|
||||
use close::*;
|
||||
use deploy::*;
|
||||
use deposit::*;
|
||||
// use initialize::*;
|
||||
@@ -55,6 +57,7 @@ pub fn process_instruction(
|
||||
OreInstruction::ClaimORE => process_claim_ore(accounts, data)?,
|
||||
OreInstruction::Deploy => process_deploy(accounts, data)?,
|
||||
OreInstruction::Log => process_log(accounts, data)?,
|
||||
OreInstruction::Close => process_close(accounts, data)?,
|
||||
// OreInstruction::Initialize => process_initialize(accounts, data)?,
|
||||
OreInstruction::Reset => process_reset(accounts, data)?,
|
||||
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
/// Pays out the winners and block reward.
|
||||
@@ -14,7 +14,7 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul
|
||||
signer_info.is_signer()?;
|
||||
let board = board_info
|
||||
.as_account_mut::<Board>(&ore_api::ID)?
|
||||
.assert_mut(|b| clock.slot > b.end_slot)?;
|
||||
.assert_mut(|b| clock.slot >= b.end_slot + INTERMISSION_SLOTS)?;
|
||||
let config = config_info.as_account::<Config>(&ore_api::ID)?;
|
||||
fee_collector_info
|
||||
.is_writable()?
|
||||
@@ -160,8 +160,8 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul
|
||||
start_slot: board.start_slot,
|
||||
end_slot: board.end_slot,
|
||||
winning_square: winning_square as u64,
|
||||
top_miner: Pubkey::default(), // board.top_miner,
|
||||
num_winners: 0, // square.count[winning_square],
|
||||
top_miner: Pubkey::default(), // Unknown
|
||||
num_winners: 0, // Unknown
|
||||
total_deployed: round.total_deployed,
|
||||
total_vaulted: round.total_vaulted,
|
||||
total_winnings: round.total_winnings,
|
||||
@@ -171,6 +171,32 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul
|
||||
.to_bytes(),
|
||||
)?;
|
||||
|
||||
// Reset board.
|
||||
board.round_id += 1;
|
||||
board.start_slot = clock.slot + 1;
|
||||
board.end_slot = board.start_slot + 150;
|
||||
|
||||
// Open next round account.
|
||||
create_program_account::<Round>(
|
||||
round_next_info,
|
||||
ore_program,
|
||||
signer_info,
|
||||
&ore_api::ID,
|
||||
&[ROUND, &(board.round_id + 1).to_le_bytes()],
|
||||
)?;
|
||||
let round_next = round_next_info.as_account_mut::<Round>(&ore_api::ID)?;
|
||||
round_next.id = board.round_id + 1;
|
||||
round_next.deployed = [0; 25];
|
||||
round_next.slot_hash = [0; 32];
|
||||
round_next.expires_at = board.end_slot + ONE_WEEK_SLOTS;
|
||||
round_next.rent_payer = *signer_info.key;
|
||||
round_next.motherlode = 0;
|
||||
round_next.top_miner = Pubkey::default();
|
||||
round_next.top_miner_reward = round.top_miner_reward;
|
||||
round_next.total_deployed = 0;
|
||||
round_next.total_vaulted = 0;
|
||||
round_next.total_winnings = 0;
|
||||
|
||||
// Do SOL transfers.
|
||||
round_info.send(total_admin_fee, &fee_collector_info);
|
||||
round_info.send(vault_amount, &treasury_info);
|
||||
|
||||
Reference in New Issue
Block a user