mirror of
https://github.com/d0zingcat/ore.git
synced 2026-05-16 07:26:51 +00:00
refactor
This commit is contained in:
133
core/api/src/consts.rs
Normal file
133
core/api/src/consts.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
use array_const_fn_init::array_const_fn_init;
|
||||
use const_crypto::ed25519;
|
||||
use solana_program::{pubkey, pubkey::Pubkey};
|
||||
|
||||
/// The reward rate to intialize the program with.
|
||||
pub const INITIAL_BASE_REWARD_RATE: u64 = 10u64.pow(3u32);
|
||||
|
||||
/// The admin allowed to initialize the program.
|
||||
pub const INITIAL_ADMIN: Pubkey = pubkey!("HBUh9g46wk2X89CvaNN15UmsznP59rh6od1h8JwYAopk");
|
||||
|
||||
/// The spam/liveness tolerance in seconds.
|
||||
pub const TOLERANCE: i64 = 5;
|
||||
|
||||
/// The minimum difficulty required of all submitted hashes.
|
||||
pub const MIN_DIFFICULTY: u32 = 8;
|
||||
|
||||
/// The decimal precision of the ORE token.
|
||||
/// There are 100 billion indivisible units per ORE (called "grains").
|
||||
pub const TOKEN_DECIMALS: u8 = 11;
|
||||
|
||||
/// The decimal precision of the ORE v1 token.
|
||||
pub const TOKEN_DECIMALS_V1: u8 = 9;
|
||||
|
||||
/// One ORE token, denominated in indivisible units.
|
||||
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 number of minutes in a program epoch.
|
||||
pub const EPOCH_MINUTES: i64 = 1;
|
||||
|
||||
/// The duration of a program epoch, in seconds.
|
||||
pub const EPOCH_DURATION: i64 = ONE_MINUTE.saturating_mul(EPOCH_MINUTES);
|
||||
|
||||
/// The maximum token supply (21 million).
|
||||
pub const MAX_SUPPLY: u64 = ONE_ORE.saturating_mul(21_000_000);
|
||||
|
||||
/// The target quantity of ORE to be mined per epoch.
|
||||
pub const TARGET_EPOCH_REWARDS: u64 = ONE_ORE.saturating_mul(EPOCH_MINUTES as u64);
|
||||
|
||||
/// The maximum quantity of ORE that can be mined per epoch.
|
||||
/// Inflation rate ≈ 1 ORE / min (min 0, max 8)
|
||||
pub const MAX_EPOCH_REWARDS: u64 = TARGET_EPOCH_REWARDS.saturating_mul(BUS_COUNT as u64);
|
||||
|
||||
/// The quantity of ORE each bus is allowed to issue per epoch.
|
||||
pub const BUS_EPOCH_REWARDS: u64 = MAX_EPOCH_REWARDS.saturating_div(BUS_COUNT as u64);
|
||||
|
||||
/// The number of bus accounts, for parallelizing mine operations.
|
||||
pub const BUS_COUNT: usize = 8;
|
||||
|
||||
/// The smoothing factor for reward rate changes. The reward rate cannot change by more or less
|
||||
/// than a factor of this constant from one epoch to the next.
|
||||
pub const SMOOTHING_FACTOR: u64 = 2;
|
||||
|
||||
// Assert MAX_EPOCH_REWARDS is evenly divisible by BUS_COUNT.
|
||||
static_assertions::const_assert!(
|
||||
(MAX_EPOCH_REWARDS / BUS_COUNT as u64) * BUS_COUNT as u64 == MAX_EPOCH_REWARDS
|
||||
);
|
||||
|
||||
/// The seed of the bus account PDA.
|
||||
pub const BUS: &[u8] = b"bus";
|
||||
|
||||
/// The seed of the config account PDA.
|
||||
pub const CONFIG: &[u8] = b"config";
|
||||
|
||||
/// The seed of the metadata account PDA.
|
||||
pub const METADATA: &[u8] = b"metadata";
|
||||
|
||||
/// The seed of the mint account PDA.
|
||||
pub const MINT: &[u8] = b"mint";
|
||||
|
||||
/// The seed of proof account PDAs.
|
||||
pub const PROOF: &[u8] = b"proof";
|
||||
|
||||
/// The seed of the treasury account PDA.
|
||||
pub const TREASURY: &[u8] = b"treasury";
|
||||
|
||||
/// Noise for deriving the mint pda
|
||||
pub const MINT_NOISE: [u8; 16] = [
|
||||
166, 199, 85, 221, 225, 119, 21, 185, 160, 82, 242, 237, 194, 84, 250, 252,
|
||||
];
|
||||
|
||||
/// The name for token metadata.
|
||||
pub const METADATA_NAME: &str = "ORE";
|
||||
|
||||
/// The ticker symbol for token metadata.
|
||||
pub const METADATA_SYMBOL: &str = "ORE";
|
||||
|
||||
/// The uri for token metdata.
|
||||
pub const METADATA_URI: &str = "https://ore.supply/metadata.json";
|
||||
|
||||
/// Program id for const pda derivations
|
||||
const PROGRAM_ID: [u8; 32] = unsafe { *(&crate::id() as *const Pubkey as *const [u8; 32]) };
|
||||
|
||||
/// The addresses of the bus accounts.
|
||||
pub const BUS_ADDRESSES: [Pubkey; BUS_COUNT] = array_const_fn_init![const_bus_address; 8];
|
||||
|
||||
/// Function to derive const bus addresses.
|
||||
const fn const_bus_address(i: usize) -> Pubkey {
|
||||
Pubkey::new_from_array(ed25519::derive_program_address(&[BUS, &[i as u8]], &PROGRAM_ID).0)
|
||||
}
|
||||
|
||||
/// The address of the config account.
|
||||
pub const CONFIG_ADDRESS: Pubkey =
|
||||
Pubkey::new_from_array(ed25519::derive_program_address(&[CONFIG], &PROGRAM_ID).0);
|
||||
|
||||
/// The address of the mint metadata account.
|
||||
pub const METADATA_ADDRESS: Pubkey = Pubkey::new_from_array(
|
||||
ed25519::derive_program_address(
|
||||
&[
|
||||
METADATA,
|
||||
unsafe { &*(&mpl_token_metadata::ID as *const Pubkey as *const [u8; 32]) },
|
||||
unsafe { &*(&MINT_ADDRESS as *const Pubkey as *const [u8; 32]) },
|
||||
],
|
||||
unsafe { &*(&mpl_token_metadata::ID as *const Pubkey as *const [u8; 32]) },
|
||||
)
|
||||
.0,
|
||||
);
|
||||
|
||||
/// The address of the mint account.
|
||||
pub const MINT_ADDRESS: Pubkey =
|
||||
Pubkey::new_from_array(ed25519::derive_program_address(&[MINT, &MINT_NOISE], &PROGRAM_ID).0);
|
||||
|
||||
/// The address of the v1 mint account.
|
||||
pub const MINT_V1_ADDRESS: Pubkey = pubkey!("oreoN2tQbHXVaZsr3pf66A48miqcBXCDJozganhEJgz");
|
||||
|
||||
/// The address of the treasury account.
|
||||
pub const TREASURY_ADDRESS: Pubkey =
|
||||
Pubkey::new_from_array(ed25519::derive_program_address(&[TREASURY], &PROGRAM_ID).0);
|
||||
|
||||
/// The bump of the treasury account, for cpis.
|
||||
pub const TREASURY_BUMP: u8 = ed25519::derive_program_address(&[TREASURY], &PROGRAM_ID).1;
|
||||
32
core/api/src/error.rs
Normal file
32
core/api/src/error.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use num_enum::IntoPrimitive;
|
||||
use solana_program::program_error::ProgramError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum OreError {
|
||||
#[error("The epoch has ended and needs reset")]
|
||||
NeedsReset = 0,
|
||||
#[error("The provided hash is invalid")]
|
||||
HashInvalid = 1,
|
||||
#[error("The provided hash did not satisfy the minimum required difficulty")]
|
||||
HashTooEasy = 2,
|
||||
#[error("The claim amount cannot be greater than the claimable rewards")]
|
||||
ClaimTooLarge = 3,
|
||||
#[error("The clock time is invalid")]
|
||||
ClockInvalid = 4,
|
||||
#[error("You are trying to submit too soon")]
|
||||
Spam = 5,
|
||||
#[error("Only one hash may be validated per transaction")]
|
||||
TransactionInvalid = 6,
|
||||
#[error("The tolerance cannot exceed i64 max value")]
|
||||
ToleranceOverflow = 7,
|
||||
#[error("The maximum supply has been reached")]
|
||||
MaxSupply = 8,
|
||||
}
|
||||
|
||||
impl From<OreError> for ProgramError {
|
||||
fn from(e: OreError) -> Self {
|
||||
ProgramError::Custom(e as u32)
|
||||
}
|
||||
}
|
||||
439
core/api/src/instruction.rs
Normal file
439
core/api/src/instruction.rs
Normal file
@@ -0,0 +1,439 @@
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use drillx::Solution;
|
||||
use num_enum::TryFromPrimitive;
|
||||
use shank::ShankInstruction;
|
||||
use solana_program::{
|
||||
instruction::{AccountMeta, Instruction},
|
||||
pubkey::Pubkey,
|
||||
system_program, sysvar,
|
||||
};
|
||||
|
||||
use crate::{consts::*, impl_instruction_from_bytes, impl_to_bytes};
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, ShankInstruction, TryFromPrimitive)]
|
||||
#[rustfmt::skip]
|
||||
pub enum OreInstruction {
|
||||
#[account(0, name = "ore_program", desc = "Ore program")]
|
||||
#[account(1, name = "signer", desc = "Signer", signer)]
|
||||
#[account(2, name = "beneficiary", desc = "Beneficiary token account", writable)]
|
||||
#[account(3, name = "proof", desc = "Ore proof account", writable)]
|
||||
#[account(4, name = "treasury", desc = "Ore treasury account", writable)]
|
||||
#[account(5, name = "treasury_tokens", desc = "Ore treasury token account", writable)]
|
||||
#[account(6, name = "token_program", desc = "SPL token program")]
|
||||
Claim = 0,
|
||||
|
||||
#[account(0, name = "ore_program", desc = "Ore program")]
|
||||
#[account(1, name = "signer", desc = "Signer", signer)]
|
||||
#[account(2, name = "proof", desc = "Ore proof account", writable)]
|
||||
#[account(3, name = "system_program", desc = "Solana system program")]
|
||||
Close = 1,
|
||||
|
||||
#[account(0, name = "ore_program", desc = "Ore program")]
|
||||
#[account(1, name = "signer", desc = "Signer", signer)]
|
||||
#[account(2, name = "config", desc = "Ore config account", writable)]
|
||||
#[account(3, name = "proof", desc = "Ore proof account – current top staker")]
|
||||
#[account(4, name = "proof_new", desc = "Ore proof account – new top staker")]
|
||||
Crown = 2,
|
||||
|
||||
#[account(0, name = "ore_program", desc = "Ore program")]
|
||||
#[account(1, name = "signer", desc = "Signer", signer)]
|
||||
#[account(2, name = "bus", desc = "Ore bus account", writable)]
|
||||
#[account(3, name = "config", desc = "Ore config account")]
|
||||
#[account(4, name = "noise", desc = "Ore noise account")]
|
||||
#[account(5, name = "proof", desc = "Ore proof account", writable)]
|
||||
#[account(6, name = "slot_hashes", desc = "Solana slot hashes sysvar")]
|
||||
Mine = 3,
|
||||
|
||||
#[account(0, name = "ore_program", desc = "Ore program")]
|
||||
#[account(1, name = "signer", desc = "Signer", signer)]
|
||||
#[account(2, name = "proof", desc = "Ore proof account", writable)]
|
||||
#[account(3, name = "system_program", desc = "Solana system program")]
|
||||
Open = 4,
|
||||
|
||||
#[account(0, name = "ore_program", desc = "Ore program")]
|
||||
#[account(1, name = "signer", desc = "Signer", signer)]
|
||||
#[account(2, name = "bus_0", desc = "Ore bus account 0", writable)]
|
||||
#[account(3, name = "bus_1", desc = "Ore bus account 1", writable)]
|
||||
#[account(4, name = "bus_2", desc = "Ore bus account 2", writable)]
|
||||
#[account(5, name = "bus_3", desc = "Ore bus account 3", writable)]
|
||||
#[account(6, name = "bus_4", desc = "Ore bus account 4", writable)]
|
||||
#[account(7, name = "bus_5", desc = "Ore bus account 5", writable)]
|
||||
#[account(8, name = "bus_6", desc = "Ore bus account 6", writable)]
|
||||
#[account(9, name = "bus_7", desc = "Ore bus account 7", writable)]
|
||||
#[account(10, name = "config", desc = "Ore config account")]
|
||||
#[account(11, name = "mint", desc = "Ore token mint account", writable)]
|
||||
#[account(12, name = "treasury", desc = "Ore treasury account", writable)]
|
||||
#[account(13, name = "treasury_tokens", desc = "Ore treasury token account", writable)]
|
||||
#[account(14, name = "token_program", desc = "SPL token program")]
|
||||
Reset = 5,
|
||||
|
||||
#[account(0, name = "ore_program", desc = "Ore program")]
|
||||
#[account(1, name = "signer", desc = "Signer", signer)]
|
||||
#[account(2, name = "proof", desc = "Ore proof account", writable)]
|
||||
#[account(3, name = "sender", desc = "Signer token account", writable)]
|
||||
#[account(4, name = "treasury_tokens", desc = "Ore treasury token account", writable)]
|
||||
#[account(5, name = "token_program", desc = "SPL token program")]
|
||||
Stake = 6,
|
||||
|
||||
#[account(0, name = "ore_program", desc = "Ore program")]
|
||||
#[account(1, name = "signer", desc = "Signer", signer)]
|
||||
#[account(2, name = "proof", desc = "Ore proof account", writable)]
|
||||
Update = 7,
|
||||
|
||||
#[account(0, name = "ore_program", desc = "Ore program")]
|
||||
#[account(1, name = "signer", desc = "Signer", signer)]
|
||||
#[account(2, name = "beneficiary", desc = "Beneficiary token account", writable)]
|
||||
#[account(3, name = "sender", desc = "Signer token account", writable)]
|
||||
#[account(4, name = "treasury", desc = "Ore treasury account", writable)]
|
||||
#[account(5, name = "mint", desc = "Ore token mint account", writable)]
|
||||
#[account(6, name = "mint_v1", desc = "Ore v1 token mint account", writable)]
|
||||
#[account(7, name = "token_program", desc = "SPL token program")]
|
||||
Upgrade = 8,
|
||||
|
||||
#[account(0, name = "ore_program", desc = "Ore program")]
|
||||
#[account(1, name = "signer", desc = "Admin signer", signer)]
|
||||
#[account(2, name = "bus_0", desc = "Ore bus account 0", writable)]
|
||||
#[account(3, name = "bus_1", desc = "Ore bus account 1", writable)]
|
||||
#[account(4, name = "bus_2", desc = "Ore bus account 2", writable)]
|
||||
#[account(5, name = "bus_3", desc = "Ore bus account 3", writable)]
|
||||
#[account(6, name = "bus_4", desc = "Ore bus account 4", writable)]
|
||||
#[account(7, name = "bus_5", desc = "Ore bus account 5", writable)]
|
||||
#[account(8, name = "bus_6", desc = "Ore bus account 6", writable)]
|
||||
#[account(9, name = "bus_7", desc = "Ore bus account 7", writable)]
|
||||
#[account(10, name = "metadata", desc = "Ore mint metadata account", writable)]
|
||||
#[account(11, name = "mint", desc = "Ore mint account", writable)]
|
||||
#[account(12, name = "noise", desc = "Ore noise account", writable)]
|
||||
#[account(13, name = "treasury", desc = "Ore treasury account", writable)]
|
||||
#[account(14, name = "treasury_tokens", desc = "Ore treasury token account", writable)]
|
||||
#[account(15, name = "system_program", desc = "Solana system program")]
|
||||
#[account(16, name = "token_program", desc = "SPL token program")]
|
||||
#[account(17, name = "associated_token_program", desc = "SPL associated token program")]
|
||||
#[account(18, name = "mpl_metadata_program", desc = "Metaplex metadata program")]
|
||||
#[account(19, name = "rent", desc = "Solana rent sysvar")]
|
||||
Initialize = 100,
|
||||
}
|
||||
|
||||
impl OreInstruction {
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
vec![*self as u8]
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
pub struct InitializeArgs {
|
||||
pub bus_0_bump: u8,
|
||||
pub bus_1_bump: u8,
|
||||
pub bus_2_bump: u8,
|
||||
pub bus_3_bump: u8,
|
||||
pub bus_4_bump: u8,
|
||||
pub bus_5_bump: u8,
|
||||
pub bus_6_bump: u8,
|
||||
pub bus_7_bump: u8,
|
||||
pub config_bump: u8,
|
||||
pub metadata_bump: u8,
|
||||
pub mint_bump: u8,
|
||||
pub treasury_bump: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
pub struct OpenArgs {
|
||||
pub bump: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
pub struct MineArgs {
|
||||
pub digest: [u8; 16],
|
||||
pub nonce: [u8; 8],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
pub struct ClaimArgs {
|
||||
pub amount: [u8; 8],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
pub struct StakeArgs {
|
||||
pub amount: [u8; 8],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
pub struct UpgradeArgs {
|
||||
pub amount: [u8; 8],
|
||||
}
|
||||
|
||||
impl_to_bytes!(InitializeArgs);
|
||||
impl_to_bytes!(OpenArgs);
|
||||
impl_to_bytes!(MineArgs);
|
||||
impl_to_bytes!(ClaimArgs);
|
||||
impl_to_bytes!(StakeArgs);
|
||||
impl_to_bytes!(UpgradeArgs);
|
||||
|
||||
impl_instruction_from_bytes!(InitializeArgs);
|
||||
impl_instruction_from_bytes!(OpenArgs);
|
||||
impl_instruction_from_bytes!(MineArgs);
|
||||
impl_instruction_from_bytes!(ClaimArgs);
|
||||
impl_instruction_from_bytes!(StakeArgs);
|
||||
impl_instruction_from_bytes!(UpgradeArgs);
|
||||
|
||||
/// Builds a claim instruction.
|
||||
pub fn claim(signer: Pubkey, beneficiary: Pubkey, amount: u64) -> Instruction {
|
||||
let proof = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id()).0;
|
||||
let treasury_tokens = spl_associated_token_account::get_associated_token_address(
|
||||
&TREASURY_ADDRESS,
|
||||
&MINT_ADDRESS,
|
||||
);
|
||||
Instruction {
|
||||
program_id: crate::id(),
|
||||
accounts: vec![
|
||||
AccountMeta::new(signer, true),
|
||||
AccountMeta::new(beneficiary, false),
|
||||
AccountMeta::new(MINT_ADDRESS, false),
|
||||
AccountMeta::new(proof, false),
|
||||
AccountMeta::new_readonly(TREASURY_ADDRESS, false),
|
||||
AccountMeta::new(treasury_tokens, false),
|
||||
AccountMeta::new_readonly(spl_token::id(), false),
|
||||
],
|
||||
data: [
|
||||
OreInstruction::Claim.to_vec(),
|
||||
ClaimArgs {
|
||||
amount: amount.to_le_bytes(),
|
||||
}
|
||||
.to_bytes()
|
||||
.to_vec(),
|
||||
]
|
||||
.concat(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a close instruction.
|
||||
pub fn close(signer: Pubkey) -> Instruction {
|
||||
let proof_pda = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id());
|
||||
Instruction {
|
||||
program_id: crate::id(),
|
||||
accounts: vec![
|
||||
AccountMeta::new(signer, true),
|
||||
AccountMeta::new(proof_pda.0, false),
|
||||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
||||
],
|
||||
data: OreInstruction::Close.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a mine instruction.
|
||||
pub fn mine(signer: Pubkey, bus: Pubkey, solution: Solution) -> Instruction {
|
||||
let proof = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id()).0;
|
||||
Instruction {
|
||||
program_id: crate::id(),
|
||||
accounts: vec![
|
||||
AccountMeta::new(signer, true),
|
||||
AccountMeta::new(bus, false),
|
||||
AccountMeta::new_readonly(CONFIG_ADDRESS, false),
|
||||
AccountMeta::new(proof, false),
|
||||
AccountMeta::new_readonly(sysvar::instructions::id(), false),
|
||||
AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
|
||||
],
|
||||
data: [
|
||||
OreInstruction::Mine.to_vec(),
|
||||
MineArgs {
|
||||
digest: solution.d,
|
||||
nonce: solution.n,
|
||||
}
|
||||
.to_bytes()
|
||||
.to_vec(),
|
||||
]
|
||||
.concat(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds an open instruction.
|
||||
pub fn open(signer: Pubkey, miner: Pubkey) -> Instruction {
|
||||
let proof_pda = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id());
|
||||
Instruction {
|
||||
program_id: crate::id(),
|
||||
accounts: vec![
|
||||
AccountMeta::new(signer, true),
|
||||
AccountMeta::new_readonly(miner, false),
|
||||
AccountMeta::new(proof_pda.0, false),
|
||||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
||||
AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
|
||||
],
|
||||
data: [
|
||||
OreInstruction::Open.to_vec(),
|
||||
OpenArgs { bump: proof_pda.1 }.to_bytes().to_vec(),
|
||||
]
|
||||
.concat(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a reset instruction.
|
||||
pub fn reset(signer: Pubkey) -> Instruction {
|
||||
let treasury_tokens = spl_associated_token_account::get_associated_token_address(
|
||||
&TREASURY_ADDRESS,
|
||||
&MINT_ADDRESS,
|
||||
);
|
||||
Instruction {
|
||||
program_id: crate::id(),
|
||||
accounts: vec![
|
||||
AccountMeta::new(signer, true),
|
||||
AccountMeta::new(BUS_ADDRESSES[0], false),
|
||||
AccountMeta::new(BUS_ADDRESSES[1], false),
|
||||
AccountMeta::new(BUS_ADDRESSES[2], false),
|
||||
AccountMeta::new(BUS_ADDRESSES[3], false),
|
||||
AccountMeta::new(BUS_ADDRESSES[4], false),
|
||||
AccountMeta::new(BUS_ADDRESSES[5], false),
|
||||
AccountMeta::new(BUS_ADDRESSES[6], false),
|
||||
AccountMeta::new(BUS_ADDRESSES[7], false),
|
||||
AccountMeta::new(CONFIG_ADDRESS, false),
|
||||
AccountMeta::new(MINT_ADDRESS, false),
|
||||
AccountMeta::new(TREASURY_ADDRESS, false),
|
||||
AccountMeta::new(treasury_tokens, false),
|
||||
AccountMeta::new_readonly(spl_token::id(), false),
|
||||
],
|
||||
data: OreInstruction::Reset.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a stake instruction.
|
||||
pub fn stake(signer: Pubkey, sender: Pubkey, amount: u64) -> Instruction {
|
||||
let proof = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id()).0;
|
||||
let treasury_tokens = spl_associated_token_account::get_associated_token_address(
|
||||
&TREASURY_ADDRESS,
|
||||
&MINT_ADDRESS,
|
||||
);
|
||||
Instruction {
|
||||
program_id: crate::id(),
|
||||
accounts: vec![
|
||||
AccountMeta::new(signer, true),
|
||||
AccountMeta::new(proof, false),
|
||||
AccountMeta::new(sender, false),
|
||||
AccountMeta::new(treasury_tokens, false),
|
||||
AccountMeta::new_readonly(spl_token::id(), false),
|
||||
],
|
||||
data: [
|
||||
OreInstruction::Stake.to_vec(),
|
||||
StakeArgs {
|
||||
amount: amount.to_le_bytes(),
|
||||
}
|
||||
.to_bytes()
|
||||
.to_vec(),
|
||||
]
|
||||
.concat(),
|
||||
}
|
||||
}
|
||||
|
||||
// Build an update instruction.
|
||||
pub fn update(signer: Pubkey, miner: Pubkey) -> Instruction {
|
||||
let proof = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id()).0;
|
||||
Instruction {
|
||||
program_id: crate::id(),
|
||||
accounts: vec![
|
||||
AccountMeta::new(signer, true),
|
||||
AccountMeta::new_readonly(miner, false),
|
||||
AccountMeta::new(proof, false),
|
||||
],
|
||||
data: OreInstruction::Update.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
// Build an upgrade instruction.
|
||||
pub fn upgrade(signer: Pubkey, beneficiary: Pubkey, sender: Pubkey, amount: u64) -> Instruction {
|
||||
Instruction {
|
||||
program_id: crate::id(),
|
||||
accounts: vec![
|
||||
AccountMeta::new(signer, true),
|
||||
AccountMeta::new(beneficiary, false),
|
||||
AccountMeta::new(MINT_ADDRESS, false),
|
||||
AccountMeta::new(MINT_V1_ADDRESS, false),
|
||||
AccountMeta::new(sender, false),
|
||||
AccountMeta::new(TREASURY_ADDRESS, false),
|
||||
AccountMeta::new_readonly(spl_token::id(), false),
|
||||
],
|
||||
data: [
|
||||
OreInstruction::Upgrade.to_vec(),
|
||||
UpgradeArgs {
|
||||
amount: amount.to_le_bytes(),
|
||||
}
|
||||
.to_bytes()
|
||||
.to_vec(),
|
||||
]
|
||||
.concat(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds an initialize instruction.
|
||||
pub fn initialize(signer: Pubkey) -> Instruction {
|
||||
let bus_pdas = [
|
||||
Pubkey::find_program_address(&[BUS, &[0]], &crate::id()),
|
||||
Pubkey::find_program_address(&[BUS, &[1]], &crate::id()),
|
||||
Pubkey::find_program_address(&[BUS, &[2]], &crate::id()),
|
||||
Pubkey::find_program_address(&[BUS, &[3]], &crate::id()),
|
||||
Pubkey::find_program_address(&[BUS, &[4]], &crate::id()),
|
||||
Pubkey::find_program_address(&[BUS, &[5]], &crate::id()),
|
||||
Pubkey::find_program_address(&[BUS, &[6]], &crate::id()),
|
||||
Pubkey::find_program_address(&[BUS, &[7]], &crate::id()),
|
||||
];
|
||||
let config_pda = Pubkey::find_program_address(&[CONFIG], &crate::id());
|
||||
let mint_pda = Pubkey::find_program_address(&[MINT, MINT_NOISE.as_slice()], &crate::id());
|
||||
let treasury_pda = Pubkey::find_program_address(&[TREASURY], &crate::id());
|
||||
let treasury_tokens =
|
||||
spl_associated_token_account::get_associated_token_address(&treasury_pda.0, &mint_pda.0);
|
||||
let metadata_pda = Pubkey::find_program_address(
|
||||
&[
|
||||
METADATA,
|
||||
mpl_token_metadata::ID.as_ref(),
|
||||
mint_pda.0.as_ref(),
|
||||
],
|
||||
&mpl_token_metadata::ID,
|
||||
);
|
||||
Instruction {
|
||||
program_id: crate::id(),
|
||||
accounts: vec![
|
||||
AccountMeta::new(signer, true),
|
||||
AccountMeta::new(bus_pdas[0].0, false),
|
||||
AccountMeta::new(bus_pdas[1].0, false),
|
||||
AccountMeta::new(bus_pdas[2].0, false),
|
||||
AccountMeta::new(bus_pdas[3].0, false),
|
||||
AccountMeta::new(bus_pdas[4].0, false),
|
||||
AccountMeta::new(bus_pdas[5].0, false),
|
||||
AccountMeta::new(bus_pdas[6].0, false),
|
||||
AccountMeta::new(bus_pdas[7].0, false),
|
||||
AccountMeta::new(config_pda.0, false),
|
||||
AccountMeta::new(metadata_pda.0, false),
|
||||
AccountMeta::new(mint_pda.0, false),
|
||||
AccountMeta::new(treasury_pda.0, false),
|
||||
AccountMeta::new(treasury_tokens, false),
|
||||
AccountMeta::new_readonly(system_program::id(), false),
|
||||
AccountMeta::new_readonly(spl_token::id(), false),
|
||||
AccountMeta::new_readonly(spl_associated_token_account::id(), false),
|
||||
AccountMeta::new_readonly(mpl_token_metadata::ID, false),
|
||||
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
||||
],
|
||||
data: [
|
||||
OreInstruction::Initialize.to_vec(),
|
||||
InitializeArgs {
|
||||
bus_0_bump: bus_pdas[0].1,
|
||||
bus_1_bump: bus_pdas[1].1,
|
||||
bus_2_bump: bus_pdas[2].1,
|
||||
bus_3_bump: bus_pdas[3].1,
|
||||
bus_4_bump: bus_pdas[4].1,
|
||||
bus_5_bump: bus_pdas[5].1,
|
||||
bus_6_bump: bus_pdas[6].1,
|
||||
bus_7_bump: bus_pdas[7].1,
|
||||
config_bump: config_pda.1,
|
||||
metadata_bump: metadata_pda.1,
|
||||
mint_bump: mint_pda.1,
|
||||
treasury_bump: treasury_pda.1,
|
||||
}
|
||||
.to_bytes()
|
||||
.to_vec(),
|
||||
]
|
||||
.concat(),
|
||||
}
|
||||
}
|
||||
9
core/api/src/lib.rs
Normal file
9
core/api/src/lib.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
pub mod consts;
|
||||
pub mod error;
|
||||
pub mod instruction;
|
||||
pub mod state;
|
||||
pub mod utils;
|
||||
|
||||
use solana_program::declare_id;
|
||||
|
||||
declare_id!("mineRHF5r6S7HyD9SppBfVMXMavDkJsxwGesEvxZr2A");
|
||||
32
core/api/src/state/bus.rs
Normal file
32
core/api/src/state/bus.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use shank::ShankAccount;
|
||||
|
||||
use crate::{
|
||||
impl_account_from_bytes, impl_to_bytes,
|
||||
utils::{AccountDiscriminator, Discriminator},
|
||||
};
|
||||
|
||||
/// Bus accounts are responsible for distributing mining rewards.
|
||||
/// There are 8 busses total to minimize write-lock contention and allow for parallel mine operations.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, ShankAccount, Zeroable)]
|
||||
pub struct Bus {
|
||||
/// The ID of the bus account.
|
||||
pub id: u64,
|
||||
|
||||
/// The remaining rewards this bus has left to payout in the current epoch.
|
||||
pub rewards: u64,
|
||||
|
||||
/// The rewards this bus would have paid out in the current epoch if there no limit.
|
||||
/// Used to calculate the updated reward rate.
|
||||
pub theoretical_rewards: u64,
|
||||
}
|
||||
|
||||
impl Discriminator for Bus {
|
||||
fn discriminator() -> AccountDiscriminator {
|
||||
AccountDiscriminator::Bus
|
||||
}
|
||||
}
|
||||
|
||||
impl_to_bytes!(Bus);
|
||||
impl_account_from_bytes!(Bus);
|
||||
37
core/api/src/state/config.rs
Normal file
37
core/api/src/state/config.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use shank::ShankAccount;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
|
||||
use crate::{
|
||||
impl_account_from_bytes, impl_to_bytes,
|
||||
utils::{AccountDiscriminator, Discriminator},
|
||||
};
|
||||
|
||||
/// Config is a singleton account which manages admin configurable variables.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, ShankAccount, Zeroable)]
|
||||
pub struct Config {
|
||||
/// The admin authority with permission to update the difficulty.
|
||||
pub admin: Pubkey,
|
||||
|
||||
/// The base reward rate paid out for a hash of minimum difficulty.
|
||||
pub base_reward_rate: u64,
|
||||
|
||||
/// The timestamp of the last reset.
|
||||
pub last_reset_at: i64,
|
||||
|
||||
/// The largest known stake balance on the network.
|
||||
pub max_stake: u64,
|
||||
|
||||
/// The address of the proof account with the highest stake balance.
|
||||
pub top_staker: Pubkey,
|
||||
}
|
||||
|
||||
impl Discriminator for Config {
|
||||
fn discriminator() -> AccountDiscriminator {
|
||||
AccountDiscriminator::Config
|
||||
}
|
||||
}
|
||||
|
||||
impl_to_bytes!(Config);
|
||||
impl_account_from_bytes!(Config);
|
||||
9
core/api/src/state/mod.rs
Normal file
9
core/api/src/state/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
mod bus;
|
||||
mod config;
|
||||
mod proof;
|
||||
mod treasury;
|
||||
|
||||
pub use bus::*;
|
||||
pub use config::*;
|
||||
pub use proof::*;
|
||||
pub use treasury::*;
|
||||
50
core/api/src/state/proof.rs
Normal file
50
core/api/src/state/proof.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use shank::ShankAccount;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
|
||||
use crate::{
|
||||
impl_account_from_bytes, impl_to_bytes,
|
||||
utils::{AccountDiscriminator, Discriminator},
|
||||
};
|
||||
|
||||
/// Proof accounts track a miner's current hash, claimable rewards, and lifetime stats.
|
||||
/// Every miner is allowed one proof account which is required by the program to mine or claim rewards.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, ShankAccount, Zeroable)]
|
||||
pub struct Proof {
|
||||
/// The signer authorized to use this proof.
|
||||
pub authority: Pubkey,
|
||||
|
||||
/// The quantity of tokens this miner has staked or earned.
|
||||
pub balance: u64,
|
||||
|
||||
/// The current mining challenge.
|
||||
pub challenge: [u8; 32],
|
||||
|
||||
/// The last hash the miner provided.
|
||||
pub last_hash: [u8; 32],
|
||||
|
||||
/// The last time this account provided a hash.
|
||||
pub last_hash_at: i64,
|
||||
|
||||
/// The last time stake was deposited into this account.
|
||||
pub last_stake_at: i64,
|
||||
|
||||
/// The keypair which has permission to submit hashes for mining.
|
||||
pub miner: Pubkey,
|
||||
|
||||
/// The total lifetime hashes provided by this miner.
|
||||
pub total_hashes: u64,
|
||||
|
||||
/// The total lifetime rewards distributed to this miner.
|
||||
pub total_rewards: u64,
|
||||
}
|
||||
|
||||
impl Discriminator for Proof {
|
||||
fn discriminator() -> AccountDiscriminator {
|
||||
AccountDiscriminator::Proof
|
||||
}
|
||||
}
|
||||
|
||||
impl_to_bytes!(Proof);
|
||||
impl_account_from_bytes!(Proof);
|
||||
22
core/api/src/state/treasury.rs
Normal file
22
core/api/src/state/treasury.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use shank::ShankAccount;
|
||||
|
||||
use crate::{
|
||||
impl_account_from_bytes, impl_to_bytes,
|
||||
utils::{AccountDiscriminator, Discriminator},
|
||||
};
|
||||
|
||||
/// Treasury is a singleton account which manages all program wide variables.
|
||||
/// It is the mint authority for the Ore token and also the authority of the program-owned token account.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, ShankAccount, Zeroable)]
|
||||
pub struct Treasury {}
|
||||
|
||||
impl Discriminator for Treasury {
|
||||
fn discriminator() -> AccountDiscriminator {
|
||||
AccountDiscriminator::Treasury
|
||||
}
|
||||
}
|
||||
|
||||
impl_to_bytes!(Treasury);
|
||||
impl_account_from_bytes!(Treasury);
|
||||
156
core/api/src/utils.rs
Normal file
156
core/api/src/utils.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use solana_program::{
|
||||
account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
|
||||
pubkey::Pubkey, rent::Rent, sysvar::Sysvar,
|
||||
};
|
||||
|
||||
/// Creates a new pda
|
||||
#[inline(always)]
|
||||
pub fn create_pda<'a, 'info>(
|
||||
target_account: &'a AccountInfo<'info>,
|
||||
owner: &Pubkey,
|
||||
space: usize,
|
||||
pda_seeds: &[&[u8]],
|
||||
system_program: &'a AccountInfo<'info>,
|
||||
payer: &'a AccountInfo<'info>,
|
||||
) -> ProgramResult {
|
||||
let rent = Rent::get()?;
|
||||
if target_account.lamports().eq(&0) {
|
||||
// If balance is zero, create account
|
||||
solana_program::program::invoke_signed(
|
||||
&solana_program::system_instruction::create_account(
|
||||
payer.key,
|
||||
target_account.key,
|
||||
rent.minimum_balance(space),
|
||||
space as u64,
|
||||
owner,
|
||||
),
|
||||
&[
|
||||
payer.clone(),
|
||||
target_account.clone(),
|
||||
system_program.clone(),
|
||||
],
|
||||
&[pda_seeds],
|
||||
)?;
|
||||
} else {
|
||||
// Otherwise, if balance is nonzero:
|
||||
|
||||
// 1) transfer sufficient lamports for rent exemption
|
||||
let rent_exempt_balance = rent
|
||||
.minimum_balance(space)
|
||||
.saturating_sub(target_account.lamports());
|
||||
if rent_exempt_balance.gt(&0) {
|
||||
solana_program::program::invoke(
|
||||
&solana_program::system_instruction::transfer(
|
||||
payer.key,
|
||||
target_account.key,
|
||||
rent_exempt_balance,
|
||||
),
|
||||
&[
|
||||
payer.clone(),
|
||||
target_account.clone(),
|
||||
system_program.clone(),
|
||||
],
|
||||
)?;
|
||||
}
|
||||
|
||||
// 2) allocate space for the account
|
||||
solana_program::program::invoke_signed(
|
||||
&solana_program::system_instruction::allocate(target_account.key, space as u64),
|
||||
&[target_account.clone(), system_program.clone()],
|
||||
&[pda_seeds],
|
||||
)?;
|
||||
|
||||
// 3) assign our program as the owner
|
||||
solana_program::program::invoke_signed(
|
||||
&solana_program::system_instruction::assign(target_account.key, owner),
|
||||
&[target_account.clone(), system_program.clone()],
|
||||
&[pda_seeds],
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
|
||||
pub enum AccountDiscriminator {
|
||||
Bus = 100,
|
||||
Config = 101,
|
||||
Proof = 102,
|
||||
Treasury = 103,
|
||||
}
|
||||
|
||||
pub trait Discriminator {
|
||||
fn discriminator() -> AccountDiscriminator;
|
||||
}
|
||||
|
||||
pub trait AccountDeserialize {
|
||||
fn try_from_bytes(data: &[u8]) -> Result<&Self, ProgramError>;
|
||||
fn try_from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, ProgramError>;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_to_bytes {
|
||||
($struct_name:ident) => {
|
||||
impl $struct_name {
|
||||
pub fn to_bytes(&self) -> &[u8] {
|
||||
bytemuck::bytes_of(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_account_from_bytes {
|
||||
($struct_name:ident) => {
|
||||
impl crate::utils::AccountDeserialize for $struct_name {
|
||||
fn try_from_bytes(
|
||||
data: &[u8],
|
||||
) -> Result<&Self, solana_program::program_error::ProgramError> {
|
||||
if (Self::discriminator() as u8).ne(&data[0]) {
|
||||
return Err(solana_program::program_error::ProgramError::InvalidAccountData);
|
||||
}
|
||||
bytemuck::try_from_bytes::<Self>(&data[8..]).or(Err(
|
||||
solana_program::program_error::ProgramError::InvalidAccountData,
|
||||
))
|
||||
}
|
||||
fn try_from_bytes_mut(
|
||||
data: &mut [u8],
|
||||
) -> Result<&mut Self, solana_program::program_error::ProgramError> {
|
||||
if (Self::discriminator() as u8).ne(&data[0]) {
|
||||
return Err(solana_program::program_error::ProgramError::InvalidAccountData);
|
||||
}
|
||||
bytemuck::try_from_bytes_mut::<Self>(&mut data[8..]).or(Err(
|
||||
solana_program::program_error::ProgramError::InvalidAccountData,
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_instruction_from_bytes {
|
||||
($struct_name:ident) => {
|
||||
impl $struct_name {
|
||||
pub fn try_from_bytes(
|
||||
data: &[u8],
|
||||
) -> Result<&Self, solana_program::program_error::ProgramError> {
|
||||
bytemuck::try_from_bytes::<Self>(data).or(Err(
|
||||
solana_program::program_error::ProgramError::InvalidInstructionData,
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
|
||||
pub struct MineEvent {
|
||||
pub difficulty: u64,
|
||||
pub reward: u64,
|
||||
pub timing: i64,
|
||||
}
|
||||
|
||||
impl_to_bytes!(MineEvent);
|
||||
Reference in New Issue
Block a user