11 decimals

This commit is contained in:
Hardhat Chad
2024-04-30 17:43:04 +00:00
parent 43a01003a3
commit 8ab3c27492
8 changed files with 49 additions and 39 deletions

View File

@@ -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.

View File

@@ -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 {

View File

@@ -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");

View File

@@ -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

View File

@@ -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(&[

View File

@@ -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);

View File

@@ -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(),

View File

@@ -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 {