Files
ore/program/src/open.rs
Hardhat Chad 11d5442dd2 cleanup
2025-06-25 09:40:11 -05:00

355 lines
11 KiB
Rust

use ore_api::prelude::*;
use solana_nostd_keccak::hash;
use solana_program::program_pack::Pack;
use spl_token_2022::instruction::AuthorityType;
use steel::*;
/// Opens a new block.
pub fn process_open(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
// Parse data.
let args = Open::try_from_bytes(data)?;
let id = u64::from_le_bytes(args.id);
// Load accounts.
let clock = Clock::get()?;
let [signer_info, block_info, collateral_info, commitment_info, market_info, mint_base_info, mint_quote_info, sender_info, treasury_info, vault_base_info, vault_quote_info, system_program, token_program, associated_token_program, rent_sysvar] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
};
signer_info.is_signer()?;
block_info
.is_empty()?
.is_writable()?
.has_seeds(&[BLOCK, &id.to_le_bytes()], &ore_api::ID)?;
market_info
.is_empty()?
.is_writable()?
.has_seeds(&[MARKET, &id.to_le_bytes()], &ore_api::ID)?;
mint_base_info
.is_empty()?
.is_writable()?
.has_seeds(&[MINT, &id.to_le_bytes()], &ore_api::ID)?;
mint_quote_info.has_address(&MINT_ADDRESS)?.as_mint()?;
sender_info
.is_writable()?
.as_associated_token_account(&signer_info.key, &mint_quote_info.key)?;
treasury_info.has_address(&TREASURY_ADDRESS)?;
system_program.is_program(&system_program::ID)?;
token_program.is_program(&spl_token::ID)?;
associated_token_program.is_program(&spl_associated_token_account::ID)?;
rent_sysvar.is_sysvar(&sysvar::rent::ID)?;
// Error out if start slot is within the current period.
let start_slot = id * 1500;
let current_period_start = (clock.slot / 1500) * 1500;
let current_period_end = current_period_start + 1500;
if start_slot < current_period_end {
return Err(ProgramError::InvalidArgument);
}
// Initialize config.
create_program_account::<Block>(
block_info,
system_program,
signer_info,
&ore_api::ID,
&[BLOCK, &id.to_le_bytes()],
)?;
let block = block_info.as_account_mut::<Block>(&ore_api::ID)?;
block.id = id;
block.reward = RewardConfig {
lode_hash: [0; 32],
lode_authority: Pubkey::default(),
lode_reward: 0,
nugget_reward: 0,
nugget_threshold: NUGGET_DIFFICULTY,
};
block.slot_hash = [0; 32];
block.start_slot = start_slot;
block.total_committed = 0;
block.total_deployed = 0;
block.total_rewards = 0;
// Select reward strategy.
let noise_seed = block.id.to_le_bytes();
let noise = hash(&noise_seed);
let lode_reward = ONE_ORE * generate_lode(noise) as u64;
let target_block_reward = ONE_ORE * 10;
let expected_hashes_per_block = HASH_TOKEN_SUPPLY / 2;
let expected_qualifying_hashes = expected_hashes_per_block / 2u64.pow(NUGGET_DIFFICULTY as u32);
let difficulty_reward = (target_block_reward - lode_reward) / expected_qualifying_hashes;
block.reward.lode_reward = lode_reward;
block.reward.nugget_reward = difficulty_reward;
// Initialize market.
create_program_account::<Market>(
market_info,
system_program,
signer_info,
&ore_api::ID,
&[MARKET, &id.to_le_bytes()],
)?;
let market = market_info.as_account_mut::<Market>(&ore_api::ID)?;
market.base = TokenParams {
mint: *mint_base_info.key,
balance: HASH_TOKEN_SUPPLY,
balance_virtual: 0,
};
market.quote = TokenParams {
mint: *mint_quote_info.key,
balance: 0,
balance_virtual: VIRTUAL_LIQUIDITY,
};
market.fee = FeeParams {
rate: FEE_RATE_BPS,
uncollected: 0,
cumulative: 0,
};
market.snapshot = Snapshot {
enabled: 1,
base_balance: 0,
quote_balance: 0,
slot: 0,
};
market.id = id;
// Initialize hash token mint.
let mint_bump = mint_pda(block.id).1;
allocate_account_with_bump(
mint_base_info,
system_program,
signer_info,
spl_token::state::Mint::LEN,
&spl_token::ID,
&[MINT, &id.to_le_bytes()],
mint_bump,
)?;
initialize_mint_signed_with_bump(
mint_base_info,
block_info,
None,
token_program,
rent_sysvar,
0,
&[MINT, &id.to_le_bytes()],
mint_bump,
)?;
// TODO Initialize hash token metadata.
// Initialize collateral and commitment token accounts.
if collateral_info.data_is_empty() {
let collateral_pda = collateral_pda(id);
allocate_account_with_bump(
collateral_info,
system_program,
signer_info,
spl_token::state::Account::LEN,
&spl_token::ID,
&[
block_info.key.as_ref(),
token_program.key.as_ref(),
mint_quote_info.key.as_ref(),
],
collateral_pda.1,
)?;
solana_program::program::invoke(
&spl_token_2022::instruction::initialize_account3(
&spl_token::ID,
&collateral_pda.0,
&mint_quote_info.key,
&block_info.key,
)?,
&[
collateral_info.clone(),
mint_quote_info.clone(),
block_info.clone(),
token_program.clone(),
],
)?;
} else {
collateral_info
.has_address(&collateral_pda(id).0)?
.as_token_account()?
.assert(|t| t.mint() == *mint_quote_info.key)?
.assert(|t| t.owner() == *block_info.key)?;
}
if commitment_info.data_is_empty() {
let commitment_pda = commitment_pda(id);
allocate_account_with_bump(
commitment_info,
system_program,
signer_info,
spl_token::state::Account::LEN,
&spl_token::ID,
&[
block_info.key.as_ref(),
token_program.key.as_ref(),
mint_base_info.key.as_ref(),
],
commitment_pda.1,
)?;
solana_program::program::invoke(
&spl_token_2022::instruction::initialize_account3(
&spl_token::ID,
&commitment_pda.0,
&mint_base_info.key,
&block_info.key,
)?,
&[
commitment_info.clone(),
mint_base_info.clone(),
block_info.clone(),
token_program.clone(),
],
)?;
} else {
commitment_info
.has_address(&commitment_pda(id).0)?
.as_token_account()?
.assert(|t| t.mint() == *mint_base_info.key)?
.assert(|t| t.owner() == *block_info.key)?;
}
// Initialize vault token accounts.
if vault_base_info.data_is_empty() {
let vault_base_pda = vault_base_pda(id);
allocate_account_with_bump(
vault_base_info,
system_program,
signer_info,
spl_token::state::Account::LEN,
&spl_token::ID,
&[
market_info.key.as_ref(),
token_program.key.as_ref(),
mint_base_info.key.as_ref(),
],
vault_base_pda.1,
)?;
solana_program::program::invoke(
&spl_token_2022::instruction::initialize_account3(
&spl_token::ID,
&vault_base_pda.0,
&mint_base_info.key,
&market_info.key,
)?,
&[
vault_base_info.clone(),
mint_base_info.clone(),
market_info.clone(),
token_program.clone(),
],
)?;
} else {
vault_base_info
.has_address(&vault_base_pda(id).0)?
.as_token_account()?
.assert(|t| t.mint() == *mint_base_info.key)?
.assert(|t| t.owner() == *market_info.key)?;
}
if vault_quote_info.data_is_empty() {
let vault_quote_pda = vault_quote_pda(id);
allocate_account_with_bump(
vault_quote_info,
system_program,
signer_info,
spl_token::state::Account::LEN,
&spl_token::ID,
&[
market_info.key.as_ref(),
token_program.key.as_ref(),
mint_quote_info.key.as_ref(),
],
vault_quote_pda.1,
)?;
solana_program::program::invoke(
&spl_token_2022::instruction::initialize_account3(
&spl_token::ID,
&vault_quote_pda.0,
&mint_quote_info.key,
&market_info.key,
)?,
&[
vault_quote_info.clone(),
mint_quote_info.clone(),
market_info.clone(),
token_program.clone(),
],
)?;
} else {
vault_quote_info
.has_address(&vault_quote_pda(id).0)?
.as_token_account()?
.assert(|t| t.mint() == *mint_quote_info.key)?
.assert(|t| t.owner() == *market_info.key)?;
}
// Mint hash tokens to market.
mint_to_signed(
mint_base_info,
vault_base_info,
block_info,
token_program,
HASH_TOKEN_SUPPLY,
&[BLOCK, &id.to_le_bytes()],
)?;
// Burn mint authority.
set_authority_signed(
mint_base_info,
block_info,
None,
AuthorityType::MintTokens,
token_program,
&[BLOCK, &id.to_le_bytes()],
)?;
// Emit event.
OpenEvent {
disc: OreEvent::Open as u64,
id,
start_slot,
signer: *signer_info.key,
reward_config: block.reward,
liquidity_base: market.base.liquidity() as u64,
liquidity_quote: market.quote.liquidity() as u64,
ts: clock.unix_timestamp,
}
.log_return();
Ok(())
}
fn generate_lode(hash: [u8; 32]) -> u8 {
// Extract the first byte (0 to 255)
let byte_value = hash[0];
// Map to 1-10 using integer division
let reward = (byte_value / 25) + 1;
// Ensure the value doesn't exceed 10
if reward > 10 {
10
} else {
reward
}
}
#[test]
fn test_lode_rewards() {
for i in 0u64..1000 {
let noise_seed = i.to_le_bytes();
let noise = hash(&noise_seed);
let lode_reward = ONE_ORE * generate_lode(noise) as u64;
let target_block_reward = ONE_ORE * 10;
let expected_hashes_per_block = HASH_TOKEN_SUPPLY / 2;
let expected_qualifying_hashes =
expected_hashes_per_block / 2u64.pow(NUGGET_DIFFICULTY as u32);
let difficulty_reward = (target_block_reward - lode_reward) / expected_qualifying_hashes;
println!("{}: {} {}", i, lode_reward, difficulty_reward);
}
// assert!(false);
}