mirror of
https://github.com/d0zingcat/ore.git
synced 2026-05-13 15:09:57 +00:00
cleanup
This commit is contained in:
3564
Cargo.lock
generated
3564
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["api", "program"]
|
||||
members = ["api", "program", "cli"]
|
||||
|
||||
[workspace.package]
|
||||
version = "3.7.0-alpha"
|
||||
|
||||
@@ -307,11 +307,11 @@ pub fn reset(
|
||||
data: Reset {}.to_bytes(),
|
||||
}
|
||||
}
|
||||
|
||||
// let [signer_info, automation_info, board_info, miner_info, round_info, treasury_info, system_program] =
|
||||
|
||||
pub fn checkpoint(signer: Pubkey, authority: Pubkey, round_id: u64) -> Instruction {
|
||||
let miner_address = miner_pda(authority).0;
|
||||
let automation_address = automation_pda(authority).0;
|
||||
let board_address = board_pda().0;
|
||||
let round_address = round_pda(round_id).0;
|
||||
let treasury_address = TREASURY_ADDRESS;
|
||||
@@ -319,7 +319,6 @@ pub fn checkpoint(signer: Pubkey, authority: Pubkey, round_id: u64) -> Instructi
|
||||
program_id: crate::ID,
|
||||
accounts: vec![
|
||||
AccountMeta::new(signer, true),
|
||||
AccountMeta::new(automation_address, false),
|
||||
AccountMeta::new(board_address, false),
|
||||
AccountMeta::new(miner_address, false),
|
||||
AccountMeta::new(round_address, false),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use steel::*;
|
||||
|
||||
use crate::state::board_pda;
|
||||
use crate::state::{board_pda, OreAccountOLD};
|
||||
|
||||
use super::OreAccount;
|
||||
|
||||
@@ -17,6 +17,40 @@ pub struct Board {
|
||||
pub end_slot: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
|
||||
pub struct BoardOLD {
|
||||
/// The round number.
|
||||
pub id: u64,
|
||||
|
||||
/// The deployed SOL for the round.
|
||||
pub deployed: [u64; 25],
|
||||
|
||||
/// The timestamp at which the block starts mining.
|
||||
pub start_at: i64,
|
||||
|
||||
/// The slot at which the block starts trading.
|
||||
pub start_slot: u64,
|
||||
|
||||
/// The slot at which the block ends trading.
|
||||
pub end_slot: u64,
|
||||
|
||||
/// The hash of the end slot, provided by solana, used for random number generation.
|
||||
pub slot_hash: [u8; 32],
|
||||
|
||||
/// The top miner of the round.
|
||||
pub top_miner: Pubkey,
|
||||
|
||||
/// 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 Board {
|
||||
pub fn pda(&self) -> (Pubkey, u8) {
|
||||
board_pda()
|
||||
@@ -24,3 +58,4 @@ impl Board {
|
||||
}
|
||||
|
||||
account!(OreAccount, Board);
|
||||
account!(OreAccountOLD, BoardOLD);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use steel::*;
|
||||
|
||||
use crate::state::miner_pda;
|
||||
use crate::state::{miner_pda, OreAccountOLD};
|
||||
|
||||
use super::OreAccount;
|
||||
|
||||
@@ -38,6 +38,38 @@ pub struct Miner {
|
||||
pub lifetime_rewards_ore: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
|
||||
pub struct MinerOLD {
|
||||
/// The authority of this miner account.
|
||||
pub authority: Pubkey,
|
||||
|
||||
/// The miner's prospects in the current round.
|
||||
pub deployed: [u64; 25],
|
||||
|
||||
/// Unused buffer.
|
||||
#[deprecated(note = "No longer used")]
|
||||
pub buffer: [u8; 32],
|
||||
|
||||
/// The amount of SOL this miner has had refunded and may claim.
|
||||
pub refund_sol: u64,
|
||||
|
||||
/// The amount of SOL this miner can claim.
|
||||
pub rewards_sol: u64,
|
||||
|
||||
/// The amount of ORE this miner can claim.
|
||||
pub rewards_ore: u64,
|
||||
|
||||
/// The ID of the round this miner last played in.
|
||||
pub round_id: u64,
|
||||
|
||||
/// The total amount of SOL this miner has mined across all blocks.
|
||||
pub lifetime_rewards_sol: u64,
|
||||
|
||||
/// The total amount of ORE this miner has mined across all blocks.
|
||||
pub lifetime_rewards_ore: u64,
|
||||
}
|
||||
|
||||
impl Miner {
|
||||
pub fn pda(&self) -> (Pubkey, u8) {
|
||||
miner_pda(self.authority)
|
||||
@@ -45,3 +77,4 @@ impl Miner {
|
||||
}
|
||||
|
||||
account!(OreAccount, Miner);
|
||||
account!(OreAccountOLD, MinerOLD);
|
||||
|
||||
@@ -35,6 +35,13 @@ pub enum OreAccount {
|
||||
Round = 109,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
|
||||
pub enum OreAccountOLD {
|
||||
MinerOLD = 103,
|
||||
BoardOLD = 105,
|
||||
}
|
||||
|
||||
pub fn automation_pda(authority: Pubkey) -> (Pubkey, u8) {
|
||||
Pubkey::find_program_address(&[AUTOMATION, &authority.to_bytes()], &crate::ID)
|
||||
}
|
||||
|
||||
@@ -49,16 +49,16 @@ impl Round {
|
||||
round_pda(self.id)
|
||||
}
|
||||
|
||||
pub fn rng(&self) -> u64 {
|
||||
if self.slot_hash == [0; 32] {
|
||||
return 0;
|
||||
pub fn rng(&self) -> Option<u64> {
|
||||
if self.slot_hash == [0; 32] || self.slot_hash == [u8::MAX; 32] {
|
||||
return None;
|
||||
}
|
||||
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
|
||||
Some(r)
|
||||
}
|
||||
|
||||
pub fn winning_square(&self, rng: u64) -> usize {
|
||||
|
||||
147
cli/src/main.rs
147
cli/src/main.rs
@@ -80,8 +80,8 @@ async fn main() {
|
||||
"deploy_all" => {
|
||||
deploy_all(&rpc, &payer).await.unwrap();
|
||||
}
|
||||
"square" => {
|
||||
log_square(&rpc).await.unwrap();
|
||||
"round" => {
|
||||
log_round(&rpc).await.unwrap();
|
||||
}
|
||||
"seeker" => {
|
||||
log_seeker(&rpc).await.unwrap();
|
||||
@@ -130,32 +130,32 @@ async fn main() {
|
||||
// 34QyjRFFU2Vp7ZAxdNm3FRCChEMbStAh9Zf58W84q7Fh
|
||||
|
||||
async fn test_kick(rpc: &RpcClient) -> Result<(), anyhow::Error> {
|
||||
let mut kps = vec![];
|
||||
for i in 1..=20 {
|
||||
let home_dir = dirs::home_dir()
|
||||
.expect("Could not find home directory")
|
||||
.display()
|
||||
.to_string();
|
||||
let path = format!("{}/.config/solana/tester-{}.json", home_dir, i);
|
||||
kps.push(read_keypair_file(&path).unwrap());
|
||||
}
|
||||
let mut alt_miners = kps.iter().map(|kp| kp.pubkey()).collect::<Vec<Pubkey>>();
|
||||
alt_miners.push(pubkey!("pqspJ298ryBjazPAr95J9sULCVpZe3HbZTWkbC1zrkS"));
|
||||
// let mut kps = vec![];
|
||||
// for i in 1..=20 {
|
||||
// let home_dir = dirs::home_dir()
|
||||
// .expect("Could not find home directory")
|
||||
// .display()
|
||||
// .to_string();
|
||||
// let path = format!("{}/.config/solana/tester-{}.json", home_dir, i);
|
||||
// kps.push(read_keypair_file(&path).unwrap());
|
||||
// }
|
||||
// let mut alt_miners = kps.iter().map(|kp| kp.pubkey()).collect::<Vec<Pubkey>>();
|
||||
// alt_miners.push(pubkey!("pqspJ298ryBjazPAr95J9sULCVpZe3HbZTWkbC1zrkS"));
|
||||
|
||||
for (i, kp) in kps.iter().enumerate() {
|
||||
let amount = 1000 + i as u64;
|
||||
let mut squares = [false; 25];
|
||||
squares[0] = true;
|
||||
let deploy_ix = ore_api::sdk::deploy(
|
||||
kp.pubkey(),
|
||||
kp.pubkey(),
|
||||
amount,
|
||||
squares,
|
||||
alt_miners.clone(),
|
||||
);
|
||||
println!("Deploying {} to square 0 for {}", amount, kp.pubkey());
|
||||
submit_transaction_no_confirm(rpc, &kp, &[deploy_ix]).await?;
|
||||
}
|
||||
// for (i, kp) in kps.iter().enumerate() {
|
||||
// let amount = 1000 + i as u64;
|
||||
// let mut squares = [false; 25];
|
||||
// squares[0] = true;
|
||||
// let deploy_ix = ore_api::sdk::deploy(
|
||||
// kp.pubkey(),
|
||||
// kp.pubkey(),
|
||||
// amount,
|
||||
// squares,
|
||||
// alt_miners.clone(),
|
||||
// );
|
||||
// println!("Deploying {} to square 0 for {}", amount, kp.pubkey());
|
||||
// submit_transaction_no_confirm(rpc, &kp, &[deploy_ix]).await?;
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -269,15 +269,19 @@ async fn reset(
|
||||
let board = get_board(rpc).await?;
|
||||
let config = get_config(rpc).await?;
|
||||
let slot_hashes = get_slot_hashes(rpc).await?;
|
||||
let mut miners = vec![];
|
||||
if let Some(slot_hash) = slot_hashes.get(&board.end_slot) {
|
||||
let id = get_winning_square(&slot_hash.to_bytes());
|
||||
let square = get_square(rpc).await?;
|
||||
// let square = get_square(rpc).await?;
|
||||
println!("Winning square: {}", id);
|
||||
// println!("Miners: {:?}", square.miners);
|
||||
miners = square.miners[id as usize].to_vec();
|
||||
// miners = square.miners[id as usize].to_vec();
|
||||
};
|
||||
let reset_ix = ore_api::sdk::reset(payer.pubkey(), config.fee_collector, miners);
|
||||
let reset_ix = ore_api::sdk::reset(
|
||||
payer.pubkey(),
|
||||
config.fee_collector,
|
||||
board.round_id,
|
||||
Pubkey::default(),
|
||||
);
|
||||
// simulate_transaction(rpc, payer, &[reset_ix]).await;
|
||||
submit_transaction(rpc, payer, &[reset_ix]).await?;
|
||||
Ok(())
|
||||
@@ -291,11 +295,16 @@ async fn deploy(
|
||||
let amount = u64::from_str(&amount).expect("Invalid AMOUNT");
|
||||
let square_id = std::env::var("SQUARE").expect("Missing SQUARE env var");
|
||||
let square_id = u64::from_str(&square_id).expect("Invalid SQUARE");
|
||||
|
||||
let board = get_board(rpc).await?;
|
||||
let mut squares = [false; 25];
|
||||
squares[square_id as usize] = true;
|
||||
|
||||
let ix = ore_api::sdk::deploy(payer.pubkey(), payer.pubkey(), amount, squares, vec![]);
|
||||
let ix = ore_api::sdk::deploy(
|
||||
payer.pubkey(),
|
||||
payer.pubkey(),
|
||||
amount,
|
||||
board.round_id,
|
||||
squares,
|
||||
);
|
||||
submit_transaction(rpc, payer, &[ix]).await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -306,8 +315,15 @@ async fn deploy_all(
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let amount = std::env::var("AMOUNT").expect("Missing AMOUNT env var");
|
||||
let amount = u64::from_str(&amount).expect("Invalid AMOUNT");
|
||||
let board = get_board(rpc).await?;
|
||||
let squares = [true; 25];
|
||||
let ix = ore_api::sdk::deploy(payer.pubkey(), payer.pubkey(), amount, squares, vec![]);
|
||||
let ix = ore_api::sdk::deploy(
|
||||
payer.pubkey(),
|
||||
payer.pubkey(),
|
||||
board.round_id,
|
||||
amount,
|
||||
squares,
|
||||
);
|
||||
submit_transaction(rpc, payer, &[ix]).await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -416,14 +432,32 @@ async fn log_treasury(rpc: &RpcClient) -> Result<(), anyhow::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn log_square(rpc: &RpcClient) -> Result<(), anyhow::Error> {
|
||||
async fn log_round(rpc: &RpcClient) -> Result<(), anyhow::Error> {
|
||||
let id = std::env::var("ID").expect("Missing ID env var");
|
||||
let id = usize::from_str(&id).expect("Invalid ID");
|
||||
let square = get_square(rpc).await?;
|
||||
println!("Square");
|
||||
println!(" count: {:?}", square.count[id]);
|
||||
println!(" deployed: {:?}", square.deployed[id]);
|
||||
println!(" miners: {:?}", square.miners[id]);
|
||||
let id = u64::from_str(&id).expect("Invalid ID");
|
||||
let round_address = round_pda(id).0;
|
||||
let round = get_round(rpc, id).await?;
|
||||
let rng = round.rng();
|
||||
println!("Round");
|
||||
println!(" Address: {}", round_address);
|
||||
println!(" Count: {:?}", round.count);
|
||||
println!(" Deployed: {:?}", round.deployed);
|
||||
println!(" Expires at: {}", round.expires_at);
|
||||
println!(" Id: {:?}", round.id);
|
||||
println!(" Motherlode: {}", round.motherlode);
|
||||
println!(" Rent payer: {}", round.rent_payer);
|
||||
println!(" Slot hash: {:?}", round.slot_hash);
|
||||
println!(" Top miner: {:?}", round.top_miner);
|
||||
println!(" Top miner reward: {}", round.top_miner_reward);
|
||||
println!(" Total deployed: {}", round.total_deployed);
|
||||
println!(" Total vaulted: {}", round.total_vaulted);
|
||||
println!(" Total winnings: {}", round.total_winnings);
|
||||
if let Some(rng) = rng {
|
||||
println!(" Winning square: {}", round.winning_square(rng));
|
||||
}
|
||||
// if round.slot_hash != [0; 32] {
|
||||
// println!(" Winning square: {}", get_winning_square(&round.slot_hash));
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -439,10 +473,11 @@ async fn log_miner(
|
||||
println!(" address: {}", miner_address);
|
||||
println!(" authority: {}", authority);
|
||||
println!(" deployed: {:?}", miner.deployed);
|
||||
println!(" refund_sol: {}", miner.refund_sol);
|
||||
println!(" cumulative: {:?}", miner.cumulative);
|
||||
println!(" rewards_sol: {}", miner.rewards_sol);
|
||||
println!(" rewards_ore: {}", miner.rewards_ore);
|
||||
println!(" round_id: {}", miner.round_id);
|
||||
println!(" checkpoint_id: {}", miner.checkpoint_id);
|
||||
println!(" lifetime_rewards_sol: {}", miner.lifetime_rewards_sol);
|
||||
println!(" lifetime_rewards_ore: {}", miner.lifetime_rewards_ore);
|
||||
Ok(())
|
||||
@@ -489,18 +524,9 @@ async fn log_board(rpc: &RpcClient) -> Result<(), anyhow::Error> {
|
||||
fn print_board(board: Board, clock: &Clock) {
|
||||
let current_slot = clock.slot;
|
||||
println!("Board");
|
||||
println!(" Id: {:?}", board.id);
|
||||
println!(" Slot hash: {:?}", board.slot_hash);
|
||||
println!(" Id: {:?}", board.round_id);
|
||||
println!(" Start slot: {}", board.start_slot);
|
||||
println!(" End slot: {}", board.end_slot);
|
||||
println!(" deployed: {:?}", board.deployed);
|
||||
println!(" Top miner: {:?}", board.top_miner);
|
||||
println!(" Total deployed: {}", board.total_deployed);
|
||||
println!(" Total vaulted: {}", board.total_vaulted);
|
||||
println!(" Total winnings: {}", board.total_winnings);
|
||||
if board.slot_hash != [0; 32] {
|
||||
println!(" Winning square: {}", get_winning_square(&board.slot_hash));
|
||||
}
|
||||
println!(
|
||||
" Time remaining: {} sec",
|
||||
(board.end_slot.saturating_sub(current_slot) as f64) * 0.4
|
||||
@@ -544,11 +570,11 @@ async fn get_slot_hashes(rpc: &RpcClient) -> Result<SlotHashes, anyhow::Error> {
|
||||
Ok(slot_hashes)
|
||||
}
|
||||
|
||||
async fn get_square(rpc: &RpcClient) -> Result<Square, anyhow::Error> {
|
||||
let square_pda = ore_api::state::square_pda();
|
||||
let account = rpc.get_account(&square_pda.0).await?;
|
||||
let square = Square::try_from_bytes(&account.data)?;
|
||||
Ok(*square)
|
||||
async fn get_round(rpc: &RpcClient, id: u64) -> Result<Round, anyhow::Error> {
|
||||
let round_pda = ore_api::state::round_pda(id);
|
||||
let account = rpc.get_account(&round_pda.0).await?;
|
||||
let round = Round::try_from_bytes(&account.data)?;
|
||||
Ok(*round)
|
||||
}
|
||||
|
||||
async fn get_treasury(rpc: &RpcClient) -> Result<Treasury, anyhow::Error> {
|
||||
@@ -598,6 +624,11 @@ async fn get_miners(rpc: &RpcClient) -> Result<Vec<(Pubkey, Miner)>, anyhow::Err
|
||||
Ok(miners)
|
||||
}
|
||||
|
||||
async fn get_miners_old(rpc: &RpcClient) -> Result<Vec<(Pubkey, MinerOLD)>, anyhow::Error> {
|
||||
let miners = get_program_accounts::<MinerOLD>(rpc, ore_api::ID, vec![]).await?;
|
||||
Ok(miners)
|
||||
}
|
||||
|
||||
fn get_winning_square(slot_hash: &[u8]) -> u64 {
|
||||
// Use slot hash to generate a random u64
|
||||
let r1 = u64::from_le_bytes(slot_hash[0..8].try_into().unwrap());
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use ore_api::prelude::*;
|
||||
use solana_program::rent::Rent;
|
||||
use solana_program::{log::sol_log, rent::Rent};
|
||||
use steel::*;
|
||||
|
||||
/// Checkpoints a miner's rewards.
|
||||
pub fn process_checkpoint(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
||||
// Load accounts.
|
||||
let clock = Clock::get()?;
|
||||
let [signer_info, automation_info, board_info, miner_info, round_info, treasury_info, system_program] =
|
||||
accounts
|
||||
let [signer_info, board_info, miner_info, round_info, treasury_info, system_program] = accounts
|
||||
else {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
};
|
||||
@@ -40,37 +39,25 @@ pub fn process_checkpoint(accounts: &[AccountInfo<'_>], _data: &[u8]) -> Program
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Calculate bot fee permissions.
|
||||
// Get the RNG.
|
||||
let Some(r) = round.rng() else {
|
||||
// If the round has no RNG, no one wins.
|
||||
miner.checkpoint_id = miner.round_id;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// Calculate bot fee.
|
||||
let mut bot_fee = 0;
|
||||
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.
|
||||
// Anyone may checkpoint this account and collect the bot fee.
|
||||
bot_fee = miner.checkpoint_fee;
|
||||
miner.checkpoint_fee = 0;
|
||||
} else {
|
||||
// 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
|
||||
.as_account::<Automation>(&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;
|
||||
let r = round.rng();
|
||||
let winning_square = round.winning_square(r) as usize;
|
||||
if miner.deployed[winning_square] > 0 {
|
||||
// Sanity check.
|
||||
@@ -101,6 +88,8 @@ pub fn process_checkpoint(accounts: &[AccountInfo<'_>], _data: &[u8]) -> Program
|
||||
}
|
||||
}
|
||||
|
||||
sol_log("I");
|
||||
|
||||
// Checkpoint miner.
|
||||
miner.checkpoint_id = round.id;
|
||||
miner.rewards_ore += rewards_ore;
|
||||
@@ -116,8 +105,9 @@ pub fn process_checkpoint(accounts: &[AccountInfo<'_>], _data: &[u8]) -> Program
|
||||
miner_info.send(bot_fee, &signer_info);
|
||||
}
|
||||
|
||||
// TODO Round debts. Track total checkpointed and/or num miner's checkpointed.
|
||||
|
||||
// Assert round has sufficient funds for rent + debts.
|
||||
// TODO Debts
|
||||
let account_size = 8 + std::mem::size_of::<Round>();
|
||||
let required_rent = Rent::get()?.minimum_balance(account_size);
|
||||
assert!(
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
use ore_api::prelude::*;
|
||||
use solana_program::rent::Rent;
|
||||
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, board_info, rent_payer_info, round_info, system_program] = accounts else {
|
||||
let [signer_info, board_info, rent_payer_info, round_info, treasury_info, system_program] =
|
||||
accounts
|
||||
else {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
};
|
||||
signer_info.is_signer()?;
|
||||
@@ -16,8 +19,18 @@ pub fn process_close(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul
|
||||
.assert_mut(|r| r.id < board.round_id)?
|
||||
.assert_mut(|r| r.expires_at < clock.slot)? // Ensure round has expired.
|
||||
.assert_mut(|r| r.rent_payer == *rent_payer_info.key)?; // Ensure the rent payer is the correct one.
|
||||
let treasury = treasury_info.as_account_mut::<Treasury>(&ore_api::ID)?;
|
||||
system_program.is_program(&system_program::ID)?;
|
||||
|
||||
// Vault all unclaimed rewards.
|
||||
let size = 8 + std::mem::size_of::<Round>();
|
||||
let min_rent = Rent::get()?.minimum_balance(size);
|
||||
let unclaimed_sol = round_info.lamports() - min_rent;
|
||||
if unclaimed_sol > 0 {
|
||||
round_info.send(unclaimed_sol, treasury_info);
|
||||
treasury.balance += unclaimed_sol;
|
||||
}
|
||||
|
||||
// Close the account.
|
||||
round_info.close(rent_payer_info)?;
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ use ore_api::prelude::*;
|
||||
use solana_program::{keccak::hashv, log::sol_log, native_token::lamports_to_sol};
|
||||
use steel::*;
|
||||
|
||||
use crate::AUTHORIZED_ACCOUNTS;
|
||||
|
||||
/// Deploys capital to prospect on a square.
|
||||
pub fn process_deploy(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
|
||||
// Parse data.
|
||||
@@ -18,16 +20,25 @@ pub fn process_deploy(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResul
|
||||
};
|
||||
signer_info.is_signer()?;
|
||||
authority_info.is_writable()?;
|
||||
automation_info.is_writable()?;
|
||||
automation_info
|
||||
.is_writable()?
|
||||
.has_seeds(&[AUTOMATION, &authority_info.key.to_bytes()], &ore_api::ID)?;
|
||||
let board = board_info
|
||||
.as_account_mut::<Board>(&ore_api::ID)?
|
||||
.assert_mut(|b| clock.slot >= b.start_slot && clock.slot < b.end_slot)?;
|
||||
let round = round_info
|
||||
.as_account_mut::<Round>(&ore_api::ID)?
|
||||
.assert_mut(|r| r.id == board.round_id)?;
|
||||
miner_info.is_writable()?;
|
||||
miner_info
|
||||
.is_writable()?
|
||||
.has_seeds(&[MINER, &authority_info.key.to_bytes()], &ore_api::ID)?;
|
||||
system_program.is_program(&system_program::ID)?;
|
||||
|
||||
// Check whitelist
|
||||
if !AUTHORIZED_ACCOUNTS.contains(&signer_info.key) {
|
||||
return Err(trace("Not authorized", OreError::NotAuthorized.into()));
|
||||
}
|
||||
|
||||
// Check if signer is the automation executor.
|
||||
let automation = if !automation_info.data_is_empty() {
|
||||
let automation = automation_info
|
||||
@@ -68,17 +79,6 @@ pub fn process_deploy(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResul
|
||||
}
|
||||
}
|
||||
|
||||
// Log
|
||||
sol_log(
|
||||
&format!(
|
||||
"Round {}. Deploying {} SOL to {} squares",
|
||||
round.id,
|
||||
lamports_to_sol(amount),
|
||||
squares.iter().filter(|&&s| s).count(),
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
// Open miner account.
|
||||
let miner = if miner_info.data_is_empty() {
|
||||
create_program_account::<Miner>(
|
||||
@@ -127,6 +127,7 @@ pub fn process_deploy(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResul
|
||||
|
||||
// Calculate all deployments.
|
||||
let mut total_amount = 0;
|
||||
let mut total_squares = 0;
|
||||
for (square_id, &should_deploy) in squares.iter().enumerate() {
|
||||
// Skip if square index is out of bounds.
|
||||
if square_id > 24 {
|
||||
@@ -154,8 +155,9 @@ pub fn process_deploy(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResul
|
||||
round.total_deployed += amount;
|
||||
round.count[square_id] += 1;
|
||||
|
||||
// Update total amount
|
||||
// Update totals.
|
||||
total_amount += amount;
|
||||
total_squares += 1;
|
||||
|
||||
// Exit early if automation does not have enough balance for another square.
|
||||
if let Some(automation) = &automation {
|
||||
@@ -185,6 +187,17 @@ pub fn process_deploy(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResul
|
||||
round_info.collect(total_amount, &signer_info)?;
|
||||
}
|
||||
|
||||
// Log
|
||||
sol_log(
|
||||
&format!(
|
||||
"Round #{}: deploying {} SOL to {} squares",
|
||||
round.id,
|
||||
lamports_to_sol(amount),
|
||||
total_squares,
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,6 @@ mod claim_yield;
|
||||
mod close;
|
||||
mod deploy;
|
||||
mod deposit;
|
||||
mod migrate_board;
|
||||
mod migrate_miner;
|
||||
// mod initialize;
|
||||
mod log;
|
||||
mod reset;
|
||||
mod set_admin;
|
||||
@@ -31,9 +28,6 @@ use claim_yield::*;
|
||||
use close::*;
|
||||
use deploy::*;
|
||||
use deposit::*;
|
||||
use migrate_board::*;
|
||||
use migrate_miner::*;
|
||||
// use initialize::*;
|
||||
use log::*;
|
||||
use reset::*;
|
||||
use set_admin::*;
|
||||
@@ -62,7 +56,6 @@ pub fn process_instruction(
|
||||
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)?,
|
||||
|
||||
// Staker
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
use ore_api::prelude::*;
|
||||
use solana_program::slot_hashes::SlotHashes;
|
||||
use steel::*;
|
||||
|
||||
/// Pays out the winners and block reward.
|
||||
pub fn process_migrate_board(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
||||
// Load accounts.
|
||||
let clock = Clock::get()?;
|
||||
let [signer_info, board_info, system_program] = accounts else {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
};
|
||||
signer_info.is_signer()?;
|
||||
let board = board_info.as_account_mut::<Board>(&ore_api::ID)?;
|
||||
system_program.is_program(&system_program::ID)?;
|
||||
|
||||
// TODO Migrate miner account.
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
use ore_api::prelude::*;
|
||||
use solana_program::slot_hashes::SlotHashes;
|
||||
use steel::*;
|
||||
|
||||
/// Pays out the winners and block reward.
|
||||
pub fn process_migrate_miner(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
|
||||
// Load accounts.
|
||||
let clock = Clock::get()?;
|
||||
let [signer_info, miner_info, system_program] = accounts else {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
};
|
||||
signer_info.is_signer()?;
|
||||
let miner = miner_info.as_account_mut::<Miner>(&ore_api::ID)?;
|
||||
system_program.is_program(&system_program::ID)?;
|
||||
|
||||
// TODO Migrate miner account.
|
||||
// TODO Move refund_sol into rewards_sol.
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -34,12 +34,35 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul
|
||||
ore_program.is_program(&ore_api::ID)?;
|
||||
slot_hashes_sysvar.is_sysvar(&sysvar::slot_hashes::ID)?;
|
||||
|
||||
// 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.count = [0; 25];
|
||||
round_next.expires_at = clock.slot + 150 + 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 = 0;
|
||||
round_next.total_deployed = 0;
|
||||
round_next.total_vaulted = 0;
|
||||
round_next.total_winnings = 0;
|
||||
|
||||
// Sample slot hash.
|
||||
let mut r = 0;
|
||||
let (winning_square, square_deployed) =
|
||||
if let Ok(slot_hash) = get_slot_hash(board.end_slot, slot_hashes_sysvar) {
|
||||
round.slot_hash = slot_hash;
|
||||
r = round.rng();
|
||||
if let Some(rng) = round.rng() {
|
||||
r = rng;
|
||||
let winning_square = round.winning_square(r);
|
||||
let square_deployed = round.deployed[winning_square];
|
||||
(winning_square, square_deployed)
|
||||
@@ -47,6 +70,11 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul
|
||||
// Cannot get slot hash. No one wins.
|
||||
round.slot_hash = [u8::MAX; 32];
|
||||
(u64::MAX as usize, 0)
|
||||
}
|
||||
} else {
|
||||
// Cannot get slot hash. No one wins.
|
||||
round.slot_hash = [u8::MAX; 32];
|
||||
(u64::MAX as usize, 0)
|
||||
};
|
||||
|
||||
// Collect admin fees.
|
||||
@@ -54,8 +82,8 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul
|
||||
|
||||
// No one won. Vault all deployed.
|
||||
if square_deployed == 0 {
|
||||
// Update board.
|
||||
round.total_vaulted = round.total_deployed;
|
||||
// Vault all deployed.
|
||||
round.total_vaulted = round.total_deployed - total_admin_fee;
|
||||
treasury.balance += round.total_deployed - total_admin_fee;
|
||||
|
||||
// Emit event.
|
||||
@@ -78,9 +106,14 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul
|
||||
.to_bytes(),
|
||||
)?;
|
||||
|
||||
// Update board
|
||||
board.round_id += 1;
|
||||
board.start_slot = clock.slot + 1;
|
||||
board.end_slot = board.start_slot + 150;
|
||||
|
||||
// Do SOL transfers.
|
||||
board_info.send(total_admin_fee, &fee_collector_info);
|
||||
board_info.send(round.total_deployed - total_admin_fee, &treasury_info);
|
||||
round_info.send(total_admin_fee, &fee_collector_info);
|
||||
round_info.send(round.total_deployed - total_admin_fee, &treasury_info);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -153,15 +186,16 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul
|
||||
}
|
||||
|
||||
// Validate top miner.
|
||||
// TODO Safety checks here.
|
||||
let top_miner_sample = round.top_miner_sample(r, winning_square);
|
||||
let top_miner = top_miner_info
|
||||
.as_account::<Miner>(&ore_api::ID)?
|
||||
.assert(|m| m.round_id == round.id)?
|
||||
.assert(|m| {
|
||||
m.cumulative[winning_square] >= top_miner_sample
|
||||
&& top_miner_sample < m.cumulative[winning_square] + m.deployed[winning_square]
|
||||
})?;
|
||||
// TODO Safety checks here (if no one won).
|
||||
// let mut top_miner_address = Pubkey::default();
|
||||
// let top_miner_sample = round.top_miner_sample(r, winning_square);
|
||||
// let top_miner = top_miner_info
|
||||
// .as_account::<Miner>(&ore_api::ID)?
|
||||
// .assert(|m| m.round_id == round.id)?
|
||||
// .assert(|m| {
|
||||
// m.cumulative[winning_square] >= top_miner_sample
|
||||
// && top_miner_sample < m.cumulative[winning_square] + m.deployed[winning_square]
|
||||
// })?;
|
||||
|
||||
// Emit event.
|
||||
program_log(
|
||||
@@ -172,7 +206,7 @@ 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: top_miner.authority,
|
||||
top_miner: Pubkey::default(), // top_miner.authority,
|
||||
num_winners: round.count[winning_square],
|
||||
total_deployed: round.total_deployed,
|
||||
total_vaulted: round.total_vaulted,
|
||||
@@ -188,28 +222,6 @@ pub fn process_reset(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResul
|
||||
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.to_le_bytes()],
|
||||
)?;
|
||||
let round_next = round_next_info.as_account_mut::<Round>(&ore_api::ID)?;
|
||||
round_next.id = board.round_id;
|
||||
round_next.deployed = [0; 25];
|
||||
round_next.slot_hash = [0; 32];
|
||||
round_next.count = [0; 25];
|
||||
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);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use solana_program::pubkey;
|
||||
use steel::*;
|
||||
|
||||
pub const AUTHORIZED_ACCOUNTS: [Pubkey; 4] = [
|
||||
pub const AUTHORIZED_ACCOUNTS: [Pubkey; 1] = [
|
||||
pubkey!("pqspJ298ryBjazPAr95J9sULCVpZe3HbZTWkbC1zrkS"),
|
||||
pubkey!("By5JFFueXCqeqLk5MzR8ZSwFxASz3SKWX2TVfT1LTFbX"),
|
||||
pubkey!("5Nb2ibzu4bWrwis2vNVD4mJprt6KTchzW6wgbVWM2PkY"),
|
||||
pubkey!("6tUUXB6LuTE1Pzpe6sP4mZL9CNA5XQYGWYbn1oqPpKeH"),
|
||||
// pubkey!("By5JFFueXCqeqLk5MzR8ZSwFxASz3SKWX2TVfT1LTFbX"),
|
||||
// pubkey!("5Nb2ibzu4bWrwis2vNVD4mJprt6KTchzW6wgbVWM2PkY"),
|
||||
// pubkey!("6tUUXB6LuTE1Pzpe6sP4mZL9CNA5XQYGWYbn1oqPpKeH"),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user