mirror of
https://github.com/d0zingcat/ore.git
synced 2026-05-17 23:16:48 +00:00
11 decimals
This commit is contained in:
@@ -5,23 +5,23 @@ use solana_program::{pubkey, pubkey::Pubkey};
|
||||
/// The reward rate to intialize the program with.
|
||||
pub const INITIAL_BASE_REWARD_RATE: u64 = 10u64.pow(3u32);
|
||||
|
||||
// TODO Migrate to 11 decimals?
|
||||
/// The decimal precision of the ORE token.
|
||||
/// Using SI prefixes, the smallest indivisible unit of ORE is a nanoORE.
|
||||
/// 1 nanoORE = 0.000000001 ORE = one billionth of an ORE
|
||||
pub const TOKEN_DECIMALS: u8 = 9;
|
||||
/// The minimum difficulty to initialize the program with.
|
||||
pub const INITIAL_MIN_DIFFICULTY: u32 = 12;
|
||||
|
||||
/// One ORE token, denominated in units of nanoORE.
|
||||
/// The decimal precision of the ORE token.
|
||||
pub const TOKEN_DECIMALS: u8 = 11;
|
||||
|
||||
/// One ORE token, denominated in indivisible units.
|
||||
pub const ONE_ORE: u64 = 10u64.pow(TOKEN_DECIMALS as u32);
|
||||
|
||||
/// The duration of an epoch, in units of seconds.
|
||||
pub const EPOCH_DURATION: i64 = 60;
|
||||
|
||||
/// The target quantity of ORE to be mined per epoch, in units of nanoORE.
|
||||
/// The target quantity of ORE to be mined per epoch.
|
||||
/// Inflation rate ≈ 1 ORE / epoch (min 0, max 2)
|
||||
pub const TARGET_EPOCH_REWARDS: u64 = ONE_ORE;
|
||||
|
||||
/// The maximum quantity of ORE that can be mined per epoch, in units of nanoORE.
|
||||
/// The maximum quantity of ORE that can be mined per epoch.
|
||||
pub const MAX_EPOCH_REWARDS: u64 = ONE_ORE.saturating_mul(2);
|
||||
|
||||
/// The quantity of ORE each bus is allowed to issue per epoch.
|
||||
|
||||
12
src/error.rs
12
src/error.rs
@@ -10,17 +10,13 @@ pub enum OreError {
|
||||
#[error("The epoch has ended and needs reset")]
|
||||
NeedsReset = 1,
|
||||
#[error("The provided hash did not satisfy the minimum required difficulty")]
|
||||
DifficultyInsufficient = 2,
|
||||
#[error("The bus does not have enough rewards to issue at this time")]
|
||||
BusRewardsInsufficient = 3,
|
||||
HashTooEasy = 2,
|
||||
#[error("The claim amount cannot be greater than the claimable rewards")]
|
||||
ClaimTooLarge = 4,
|
||||
ClaimTooLarge = 3,
|
||||
#[error("The stake amount cannot exceed u64 size")]
|
||||
StakeTooLarge = 5,
|
||||
StakeTooLarge = 4,
|
||||
#[error("The clock time is invalid")]
|
||||
ClockInvalid = 6,
|
||||
#[error("The noise account is as large as it can get")]
|
||||
NoiseSizeExceeded = 7,
|
||||
ClockInvalid = 5,
|
||||
}
|
||||
|
||||
impl From<OreError> for ProgramError {
|
||||
|
||||
@@ -14,10 +14,8 @@ use solana_program::{
|
||||
program_error::ProgramError, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
// TODO u128 for internal rewards representation?
|
||||
// TODO Admin fn for min difficulty? What if this were set automatically by u128 base reward rate?
|
||||
// TODO Increase bus count?
|
||||
// TODO Is downgrade necessary or no?
|
||||
// TODO Admin fn for min difficulty?
|
||||
// TODO Is downgrade necessary?
|
||||
|
||||
declare_id!("mineRHF5r6S7HyD9SppBfVMXMavDkJsxwGesEvxZr2A");
|
||||
|
||||
|
||||
@@ -17,8 +17,9 @@ use crate::{
|
||||
utils::create_pda,
|
||||
utils::AccountDeserialize,
|
||||
utils::Discriminator,
|
||||
BUS, BUS_COUNT, CONFIG, INITIAL_BASE_REWARD_RATE, METADATA, METADATA_NAME, METADATA_SYMBOL,
|
||||
METADATA_URI, MINT, MINT_ADDRESS, MINT_NOISE, TOKEN_DECIMALS, TREASURY,
|
||||
BUS, BUS_COUNT, CONFIG, INITIAL_BASE_REWARD_RATE, INITIAL_MIN_DIFFICULTY, METADATA,
|
||||
METADATA_NAME, METADATA_SYMBOL, METADATA_URI, MINT, MINT_ADDRESS, MINT_NOISE, TOKEN_DECIMALS,
|
||||
TREASURY,
|
||||
};
|
||||
|
||||
/// Initialize sets up the Ore program. Its responsibilities include:
|
||||
@@ -134,7 +135,7 @@ pub fn process_initialize<'a, 'info>(
|
||||
config.admin = *signer.key;
|
||||
config.base_reward_rate = INITIAL_BASE_REWARD_RATE;
|
||||
config.last_reset_at = 0;
|
||||
config.min_difficulty = 8;
|
||||
config.min_difficulty = INITIAL_MIN_DIFFICULTY;
|
||||
config.paused = 0;
|
||||
|
||||
// Initialize treasury
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::{
|
||||
loaders::*,
|
||||
state::{Bus, Config, Proof},
|
||||
utils::AccountDeserialize,
|
||||
BUS_EPOCH_REWARDS, EPOCH_DURATION,
|
||||
EPOCH_DURATION,
|
||||
};
|
||||
|
||||
// TODO Look into tx introspection to require 1 hash per tx
|
||||
@@ -69,7 +69,7 @@ pub fn process_mine<'a, 'info>(
|
||||
return Err(OreError::ClockInvalid.into());
|
||||
}
|
||||
|
||||
// Validate epoch is active
|
||||
// TODO Validate epoch is active
|
||||
// let treasury_data = treasury_info.data.borrow();
|
||||
// let treasury = Treasury::try_from_bytes(&treasury_data)?;
|
||||
// let threshold = treasury.last_reset_at.saturating_add(EPOCH_DURATION);
|
||||
@@ -84,7 +84,7 @@ pub fn process_mine<'a, 'info>(
|
||||
let difficulty = drillx::difficulty(hx);
|
||||
sol_log(&format!("Diff {}", difficulty));
|
||||
if difficulty.le(&config.min_difficulty) {
|
||||
return Err(OreError::DifficultyInsufficient.into());
|
||||
return Err(OreError::HashTooEasy.into());
|
||||
}
|
||||
|
||||
// Calculate base reward rate
|
||||
@@ -143,16 +143,17 @@ pub fn process_mine<'a, 'info>(
|
||||
// Set upper bound to whatever is left in the bus
|
||||
let mut bus_data = bus_info.data.borrow_mut();
|
||||
let bus = Bus::try_from_bytes_mut(&mut bus_data)?;
|
||||
reward = reward.min(bus.rewards);
|
||||
let actual_reward = reward.min(bus.rewards);
|
||||
|
||||
// Update balances
|
||||
sol_log(&format!("Total {}", reward));
|
||||
sol_log(&format!("Bus {}", bus.rewards));
|
||||
bus.theoretical_rewards = bus.theoretical_rewards.saturating_add(reward);
|
||||
bus.rewards = bus
|
||||
.rewards
|
||||
.checked_sub(reward)
|
||||
.ok_or(OreError::BusRewardsInsufficient)?;
|
||||
proof.balance = proof.balance.saturating_add(reward);
|
||||
.checked_sub(actual_reward)
|
||||
.expect("This should not happen");
|
||||
proof.balance = proof.balance.saturating_add(actual_reward);
|
||||
|
||||
// Hash recent slot hash into the next challenge to prevent pre-mining attacks
|
||||
proof.challenge = hashv(&[
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::{
|
||||
/// Reset sets up the Ore program for the next epoch. Its responsibilities include:
|
||||
/// 1. Reset bus account rewards counters.
|
||||
/// 2. Adjust the reward rate to stabilize inflation.
|
||||
/// 3. Top up the treasury token account to backup claims.
|
||||
/// 3. Top up the treasury token account to fund claims.
|
||||
///
|
||||
/// Safety requirements:
|
||||
/// - Reset is a permissionless instruction and can be invoked by any signer.
|
||||
@@ -29,8 +29,11 @@ use crate::{
|
||||
/// Discussion:
|
||||
/// - It is important that `reset` can only be invoked once per 60 second period to ensure the supply growth rate
|
||||
/// stays within the guaranteed bounds of 0 ≤ R ≤ 2 ORE/min.
|
||||
/// - The reward rate is dynamically adjusted based on last epoch's actual reward rate (proxy for hashpower) to
|
||||
/// target an average supply growth rate of 1 ORE/min.
|
||||
/// - The reward rate is dynamically adjusted based on last epoch's theoretical reward rate to target an average
|
||||
/// supply growth rate of 1 ORE/min.
|
||||
/// - The "theoretical" reward rate refers to the amount that would have been paid out if rewards were not capped by
|
||||
/// the bus limits. It's necessary to use this value to ensure the reward rate update calculation accurately
|
||||
/// accounts for the difficulty of submitted hashes.
|
||||
pub fn process_reset<'a, 'info>(
|
||||
_program_id: &Pubkey,
|
||||
accounts: &'a [AccountInfo<'info>],
|
||||
@@ -85,17 +88,21 @@ pub fn process_reset<'a, 'info>(
|
||||
|
||||
// Reset bus accounts and calculate actual rewards mined since last reset
|
||||
let mut total_remaining_rewards = 0u64;
|
||||
let mut total_theoretical_rewards = 0u64;
|
||||
for i in 0..BUS_COUNT {
|
||||
let mut bus_data = busses[i].data.borrow_mut();
|
||||
let bus = Bus::try_from_bytes_mut(&mut bus_data)?;
|
||||
total_remaining_rewards = total_remaining_rewards.saturating_add(bus.rewards);
|
||||
total_theoretical_rewards =
|
||||
total_theoretical_rewards.saturating_add(bus.theoretical_rewards);
|
||||
bus.rewards = BUS_EPOCH_REWARDS;
|
||||
bus.theoretical_rewards = 0;
|
||||
}
|
||||
let total_epoch_rewards = MAX_EPOCH_REWARDS.saturating_sub(total_remaining_rewards);
|
||||
|
||||
// Update base reward rate for next epoch
|
||||
config.base_reward_rate =
|
||||
calculate_new_reward_rate(config.base_reward_rate, total_epoch_rewards);
|
||||
calculate_new_reward_rate(config.base_reward_rate, total_theoretical_rewards);
|
||||
|
||||
// Fund treasury token account
|
||||
solana_program::program::invoke_signed(
|
||||
@@ -135,9 +142,9 @@ pub(crate) fn calculate_new_reward_rate(current_rate: u64, epoch_rewards: u64) -
|
||||
}
|
||||
|
||||
// Calculate new reward rate.
|
||||
let new_rate = (current_rate)
|
||||
.saturating_mul(TARGET_EPOCH_REWARDS)
|
||||
.saturating_div(epoch_rewards) as u64;
|
||||
let new_rate = (current_rate as u128)
|
||||
.saturating_mul(TARGET_EPOCH_REWARDS as u128)
|
||||
.saturating_div(epoch_rewards as u128) as u64;
|
||||
|
||||
// Smooth reward rate so it cannot change by more than a constant factor from one epoch to the next.
|
||||
let new_rate_min = current_rate.saturating_div(SMOOTHING_FACTOR);
|
||||
|
||||
@@ -53,8 +53,11 @@ pub fn process_upgrade<'a, 'info>(
|
||||
],
|
||||
)?;
|
||||
|
||||
// Account for decimals change.
|
||||
// v1 token has 9 decimals. v2 token has 11.
|
||||
let amount_to_mint = amount.saturating_mul(100);
|
||||
|
||||
// Mint to the beneficiary account
|
||||
// TODO Account for decimals change!
|
||||
let treasury_data = treasury_info.data.borrow();
|
||||
let treasury = Treasury::try_from_bytes(&treasury_data)?;
|
||||
let treasury_bump = treasury.bump as u8;
|
||||
@@ -66,7 +69,7 @@ pub fn process_upgrade<'a, 'info>(
|
||||
beneficiary_info.key,
|
||||
treasury_info.key,
|
||||
&[treasury_info.key],
|
||||
amount,
|
||||
amount_to_mint,
|
||||
)?,
|
||||
&[
|
||||
token_program.clone(),
|
||||
|
||||
@@ -17,6 +17,10 @@ pub struct Bus {
|
||||
|
||||
/// The quantity of rewards this bus can issue in the current epoch epoch.
|
||||
pub rewards: u64,
|
||||
|
||||
// TODO Come up with better name
|
||||
/// The rewards that would have been paid out this epoch if the bus had no limit.
|
||||
pub theoretical_rewards: u64,
|
||||
}
|
||||
|
||||
impl Discriminator for Bus {
|
||||
|
||||
Reference in New Issue
Block a user