This commit is contained in:
Hardhat Chad
2024-01-22 22:42:36 +00:00
parent 0fb07fc0f4
commit f81fac5027

View File

@@ -13,18 +13,20 @@ use anchor_spl::{
token::{self, Mint, MintTo, TokenAccount},
};
// TODO Rename Metadata to Config?
// TODO Upgrade to token22
// TODO Use the confidential transfers extension.
// TODO Use the memo extension?
declare_id!("CeJShZEAzBLwtcLQvbZc7UT38e4nUTn63Za5UFyYYDTS");
/// 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;
// TODO Set this before deployment
/// The unix timestamp after which mining is allowed.
pub const START_AT: i64 = 0;
/// The initial reward rate to payout in the first epoch.
pub const INITIAL_REWARD_RATE: u64 = 10u64.pow(3u32);
/// The initial hashing difficulty. The admin authority can update this in the future, if needed.
pub const INITIAL_DIFFICULTY: Hash = Hash::new_from_array([
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
]);
/// The mint address of the ORE token.
pub const TOKEN_MINT_ADDRESS: Pubkey = Pubkey::new_from_array([
@@ -32,6 +34,11 @@ pub const TOKEN_MINT_ADDRESS: Pubkey = Pubkey::new_from_array([
132, 86, 10, 198, 221, 80, 15, 115, 222, 47, 191,
]);
/// 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;
/// One ORE token, denominated in units of nanoORE.
pub const ONE_ORE: u64 = 10u64.pow(TOKEN_DECIMALS as u32);
@@ -42,46 +49,34 @@ pub const EPOCH_DURATION: i64 = 60;
/// Inflation rate ≈ 1 ORE / epoch (min 0, max 2)
pub const TARGET_EPOCH_REWARDS: u64 = ONE_ORE;
/// The smoothing factor for reward rate changes. The reward rate cannot change by more or less
/// than factor of this constant from one epoch to the next.
pub const SMOOTHING_FACTOR: u64 = 2;
/// The maximum quantity of ORE that can be mined per epoch, in units of nanoORE.
pub const MAX_EPOCH_REWARDS: u64 = ONE_ORE.saturating_mul(2);
/// 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: u8 = 8;
/// The quantity of ORE each bus will be topped up with at the beginning of each epoch.
pub const BUS_BALANCE: u64 = TARGET_EPOCH_REWARDS
.saturating_mul(SMOOTHING_FACTOR)
.saturating_div(BUS_COUNT as u64);
/// The smoothing factor for reward rate changes. The reward rate cannot change by more or less
/// than factor of this constant from one epoch to the next.
pub const SMOOTHING_FACTOR: u64 = 2;
/// The initial hashing difficulty. The admin authority can update this in the future, if needed.
pub const INITIAL_DIFFICULTY: Hash = Hash::new_from_array([
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
]);
/// The initial reward rate to payout in the first epoch.
pub const INITIAL_REWARD_RATE: u64 = 10u64.pow(3u32);
// TODO Set this before deployment
/// The unix timestamp after which mining is allowed.
pub const START_AT: i64 = 0;
// Assert TARGET_EPOCH_REWARDS is evenly divisible by BUS_COUNT.
// Assert MAX_EPOCH_REWARDS is evenly divisible by BUS_COUNT.
static_assertions::const_assert!(
(TARGET_EPOCH_REWARDS / BUS_COUNT as u64) * BUS_COUNT as u64 == TARGET_EPOCH_REWARDS
(MAX_EPOCH_REWARDS / BUS_COUNT as u64) * BUS_COUNT as u64 == MAX_EPOCH_REWARDS
);
#[program]
mod ore {
use super::*;
/// Initializes the metadata account. Can only be invoked once.
pub fn initialize_metadata(ctx: Context<InitializeMetadata>) -> Result<()> {
ctx.accounts.metadata.bump = ctx.bumps.metadata;
ctx.accounts.metadata.admin = ctx.accounts.signer.key();
ctx.accounts.metadata.difficulty = INITIAL_DIFFICULTY;
ctx.accounts.metadata.reward_rate = INITIAL_REWARD_RATE;
/// Initializes the treasury account. Can only be invoked once.
pub fn initialize_treasury(ctx: Context<InitializeTreasury>) -> Result<()> {
ctx.accounts.treasury.bump = ctx.bumps.treasury;
ctx.accounts.treasury.admin = ctx.accounts.signer.key();
ctx.accounts.treasury.difficulty = INITIAL_DIFFICULTY;
ctx.accounts.treasury.reward_rate = INITIAL_REWARD_RATE;
Ok(())
}
@@ -106,11 +101,6 @@ mod ore {
Ok(())
}
/// Initializes an associated token account for a bus. Can only be invoked once per bus.
pub fn initialize_bus_tokens(_ctx: Context<InitializeBusTokens>) -> Result<()> {
Ok(())
}
/// Initializes a proof account for a new miner. Can only invoked once per signer.
pub fn initialize_proof(ctx: Context<InitializeProof>) -> Result<()> {
ctx.accounts.proof.authority = ctx.accounts.signer.key();
@@ -123,14 +113,15 @@ mod ore {
pub fn reset_epoch(ctx: Context<ResetEpoch>) -> Result<()> {
// Validate epoch has ended.
let clock = Clock::get().unwrap();
let metadata = &mut ctx.accounts.metadata;
let epoch_end_at = metadata.epoch_start_at.saturating_add(EPOCH_DURATION);
let treasury = &mut ctx.accounts.treasury;
let epoch_end_at = treasury.epoch_start_at.saturating_add(EPOCH_DURATION);
require!(
clock.unix_timestamp.ge(&epoch_end_at),
ProgramError::ClockInvalid
);
// Calculate total rewards issued during the epoch.
// TODO Require MAX_EPOCH_REWARDS >= remaining_available_rewards, else invalid math (fatal)...
let bus_0 = &mut ctx.accounts.bus_0;
let bus_1 = &mut ctx.accounts.bus_1;
let bus_2 = &mut ctx.accounts.bus_2;
@@ -139,79 +130,43 @@ mod ore {
let bus_5 = &mut ctx.accounts.bus_5;
let bus_6 = &mut ctx.accounts.bus_6;
let bus_7 = &mut ctx.accounts.bus_7;
let total_epoch_rewards = bus_0
.epoch_rewards
.saturating_add(bus_1.epoch_rewards)
.saturating_add(bus_2.epoch_rewards)
.saturating_add(bus_3.epoch_rewards)
.saturating_add(bus_4.epoch_rewards)
.saturating_add(bus_5.epoch_rewards)
.saturating_add(bus_6.epoch_rewards)
.saturating_add(bus_7.epoch_rewards);
let total_available_rewards = bus_0
.available_rewards
.saturating_add(bus_1.available_rewards)
.saturating_add(bus_2.available_rewards)
.saturating_add(bus_3.available_rewards)
.saturating_add(bus_4.available_rewards)
.saturating_add(bus_5.available_rewards)
.saturating_add(bus_6.available_rewards)
.saturating_add(bus_7.available_rewards);
let total_epoch_rewards = MAX_EPOCH_REWARDS.saturating_sub(total_available_rewards);
// Update the reward amount for the next epoch.
metadata.reward_rate = calculate_new_reward_rate(metadata.reward_rate, total_epoch_rewards);
metadata.epoch_start_at = clock.unix_timestamp;
treasury.reward_rate = calculate_new_reward_rate(treasury.reward_rate, total_epoch_rewards);
treasury.epoch_start_at = clock.unix_timestamp;
// Reset bus accounts.
let mint = &ctx.accounts.mint;
let metadata = &ctx.accounts.metadata;
let token_program = &ctx.accounts.token_program;
reset_bus(
bus_0,
&mut ctx.accounts.bus_0_tokens,
mint,
metadata,
token_program,
)?;
reset_bus(
bus_1,
&mut ctx.accounts.bus_1_tokens,
mint,
metadata,
token_program,
)?;
reset_bus(
bus_2,
&mut ctx.accounts.bus_2_tokens,
mint,
metadata,
token_program,
)?;
reset_bus(
bus_3,
&mut ctx.accounts.bus_3_tokens,
mint,
metadata,
token_program,
)?;
reset_bus(
bus_4,
&mut ctx.accounts.bus_4_tokens,
mint,
metadata,
token_program,
)?;
reset_bus(
bus_5,
&mut ctx.accounts.bus_5_tokens,
mint,
metadata,
token_program,
)?;
reset_bus(
bus_6,
&mut ctx.accounts.bus_6_tokens,
mint,
metadata,
token_program,
)?;
reset_bus(
bus_7,
&mut ctx.accounts.bus_7_tokens,
mint,
metadata,
token_program,
bus_0.available_rewards = BUS_EPOCH_REWARDS;
bus_1.available_rewards = BUS_EPOCH_REWARDS;
bus_2.available_rewards = BUS_EPOCH_REWARDS;
bus_3.available_rewards = BUS_EPOCH_REWARDS;
bus_4.available_rewards = BUS_EPOCH_REWARDS;
bus_5.available_rewards = BUS_EPOCH_REWARDS;
bus_6.available_rewards = BUS_EPOCH_REWARDS;
bus_7.available_rewards = BUS_EPOCH_REWARDS;
// Top up treasury token account.
token::mint_to(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
MintTo {
authority: treasury.to_account_info(),
mint: ctx.accounts.mint.to_account_info(),
to: ctx.accounts.treasury_tokens.to_account_info(),
},
&[&[TREASURY, &[treasury.bump]]],
),
total_epoch_rewards,
)?;
Ok(())
@@ -221,54 +176,77 @@ mod ore {
pub fn mine(ctx: Context<Mine>, hash: Hash, nonce: u64) -> Result<()> {
// Validate epoch is active.
let clock = Clock::get().unwrap();
let metadata = &mut ctx.accounts.metadata;
let epoch_end_at = metadata.epoch_start_at.saturating_add(EPOCH_DURATION);
let treasury = &mut ctx.accounts.treasury;
let epoch_end_at = treasury.epoch_start_at.saturating_add(EPOCH_DURATION);
require!(
clock.unix_timestamp.lt(&epoch_end_at),
ProgramError::EpochNeedsReset
);
// Validate hash.
// Validate provided hash.
let proof = &mut ctx.accounts.proof;
validate_hash(
proof.hash.clone(),
hash.clone(),
ctx.accounts.signer.key(),
nonce,
metadata.difficulty,
treasury.difficulty,
)?;
// Update bus stats.
// Update claimable rewards.
let bus = &mut ctx.accounts.bus;
bus.epoch_rewards = bus.epoch_rewards.saturating_add(metadata.reward_rate);
require!(
bus.available_rewards.ge(&treasury.reward_rate),
ProgramError::BusInsufficientFunds
);
bus.available_rewards = bus.available_rewards.saturating_sub(treasury.reward_rate);
proof.claimable_rewards = proof.claimable_rewards.saturating_add(treasury.reward_rate);
// Update miner stats.
proof.total_hashes = proof.total_hashes.saturating_add(1);
proof.total_rewards = proof.total_rewards.saturating_add(1);
// Hash a recent slot hash into the next hash to prevent pre-mining attacks.
// Hash most recent slot hash into the next challenge to prevent pre-mining attacks.
let slot_hash_bytes = &ctx.accounts.slot_hashes.data.borrow()[0..size_of::<SlotHash>()];
let slot_hash: SlotHash =
bincode::deserialize(slot_hash_bytes).expect("Failed to deserialize slot hash");
proof.hash = hashv(&[hash.as_ref(), slot_hash.1.as_ref()]);
// Distribute tokens from bus to beneficiary.
let bus_tokens = &ctx.accounts.bus_tokens;
// Update lifetime stats.
proof.total_hashes = proof.total_hashes.saturating_add(1);
proof.total_rewards = proof.total_rewards.saturating_add(1);
Ok(())
}
pub fn claim(ctx: Context<Claim>, amount: u64) -> Result<()> {
// Validate claim is for an appropriate quantity of tokens.
let proof = &mut ctx.accounts.proof;
require!(
bus_tokens.amount.ge(&metadata.reward_rate),
ProgramError::BusInsufficientFunds
amount.ge(&proof.claimable_rewards),
ProgramError::ClaimTooLarge
);
// Update claimable amount.
proof.claimable_rewards = proof.claimable_rewards.saturating_sub(amount);
// Update lifetime status.
let treasury = &mut ctx.accounts.treasury;
treasury.total_claimed_rewards = treasury.total_claimed_rewards.saturating_sub(amount);
// Distribute tokens from treasury to beneficiary.
let treasury_tokens = &ctx.accounts.treasury_tokens;
require!(
treasury_tokens.amount.ge(&amount),
ProgramError::TreasuryInsufficientFunds
);
token::transfer(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
token::Transfer {
from: bus_tokens.to_account_info(),
from: treasury_tokens.to_account_info(),
to: ctx.accounts.beneficiary.to_account_info(),
authority: bus.to_account_info(),
authority: treasury.to_account_info(),
},
&[&[BUS, &[bus.id], &[bus.bump]]],
&[&[TREASURY, &[treasury.bump]]],
),
metadata.reward_rate,
amount,
)?;
Ok(())
@@ -276,7 +254,7 @@ mod ore {
/// Updates the admin to a new value. Can only be invoked by the admin authority.
pub fn update_admin(ctx: Context<UpdateDifficulty>, new_admin: Pubkey) -> Result<()> {
ctx.accounts.metadata.admin = new_admin;
ctx.accounts.treasury.admin = new_admin;
Ok(())
}
@@ -294,7 +272,7 @@ mod ore {
/// Ore inflation rate would be challenged. So in practice, Solana is likely to reach its
/// network saturation point long before the Ore inflation hits its boundary condition.
pub fn update_difficulty(ctx: Context<UpdateDifficulty>, new_difficulty: Hash) -> Result<()> {
ctx.accounts.metadata.difficulty = new_difficulty;
ctx.accounts.treasury.difficulty = new_difficulty;
Ok(())
}
}
@@ -336,49 +314,19 @@ fn calculate_new_reward_rate(current_rate: u64, epoch_rewards: u64) -> u64 {
let new_rate_max = current_rate.saturating_mul(SMOOTHING_FACTOR);
let new_rate_smoothed = new_rate_min.max(new_rate_max.min(new_rate));
// Prevent reward rate from dropping below 1 or exceeding BUS_BALANCE and return.
new_rate_smoothed.max(1).min(BUS_BALANCE)
}
fn reset_bus<'info>(
bus: &mut Account<Bus>,
bus_tokens: &mut Account<'info, TokenAccount>,
mint: &Account<'info, Mint>,
metadata: &Account<'info, Metadata>,
token_program: &Program<'info, token::Token>,
) -> Result<()> {
// Reset bus state.
bus.epoch_rewards = 0;
// Top up bus account.
let amount = BUS_BALANCE.saturating_sub(bus_tokens.amount);
if amount.gt(&0) {
token::mint_to(
CpiContext::new_with_signer(
token_program.to_account_info(),
MintTo {
authority: metadata.to_account_info(),
mint: mint.to_account_info(),
to: bus_tokens.to_account_info(),
},
&[&[METADATA, &[metadata.bump]]],
),
amount,
)?;
}
Ok(())
// Prevent reward rate from dropping below 1 or exceeding BUS_EPOCH_REWARDS and return.
new_rate_smoothed.max(1).min(BUS_EPOCH_REWARDS)
}
/// The seed of the bus account PDA.
pub const BUS: &[u8] = b"bus";
/// The seed of the metadata account PDA.
pub const METADATA: &[u8] = b"metadata";
/// The seed of the proof account PDA.
pub const PROOF: &[u8] = b"proof";
/// The seed of the treasury account PDA.
pub const TREASURY: &[u8] = b"treasury";
/// Bus is an account type used to track the number of processed hashes and issued rewards
/// during an epoch. There are 8 bus accounts to provide sufficient parallelism for mine ops
/// and reduce write lock contention.
@@ -391,15 +339,38 @@ pub struct Bus {
/// The ID of the bus account.
pub id: u8,
/// The count of rewards issued by this bus in the current epoch.
pub epoch_rewards: u64,
/// The quantity of rewards this bus can issue in the current epoch epoch.
pub available_rewards: u64,
}
/// Metadata is an account type used to track global program variables.
/// Proof is an account type used to track a miner's hash chain.
#[account]
#[derive(Debug, PartialEq)]
pub struct Metadata {
/// The bump of the metadata account PDA.
pub struct Proof {
/// The bump of the proof account PDA.
pub bump: u8,
/// The account (i.e. miner) authorized to use this proof.
pub authority: Pubkey,
/// The quantity of tokens this miner may claim from the treasury.
pub claimable_rewards: u64,
/// The proof's current hash.
pub hash: Hash,
/// The total lifetime hashes provided by this miner.
pub total_hashes: u64,
/// The total lifetime rewards distributed to this miner.
pub total_rewards: u64,
}
/// Treasury is an account type used to track global program variables.
#[account]
#[derive(Debug, PartialEq)]
pub struct Treasury {
/// The bump of the treasury account PDA.
pub bump: u8,
/// The admin authority with permission to update the difficulty.
@@ -413,45 +384,28 @@ pub struct Metadata {
/// The reward rate to payout to miners for submiting valid hashes.
pub reward_rate: u64,
}
/// Proof is an account type used to track a miner's hash chain.
#[account]
#[derive(Debug, PartialEq)]
pub struct Proof {
/// The bump of the proof account PDA.
pub bump: u8,
/// The account (i.e. miner) authorized to use this proof.
pub authority: Pubkey,
/// The proof's current hash.
pub hash: Hash,
/// The total lifetime hashes provided by this miner.
pub total_hashes: u64,
/// The total lifetime rewards distributed to this miner.
pub total_rewards: u64,
/// The total lifetime claimed rewards.
pub total_claimed_rewards: u64,
}
#[derive(Accounts)]
pub struct InitializeMetadata<'info> {
pub struct InitializeTreasury<'info> {
/// The signer of the transaction.
#[account(mut)]
pub signer: Signer<'info>,
/// The metadata account.
#[account(init, seeds = [METADATA], bump, payer = signer, space = 8 + size_of::<Metadata>())]
pub metadata: Account<'info, Metadata>,
/// The Ore token mint.
#[account(init, address = TOKEN_MINT_ADDRESS, payer = signer, mint::decimals = TOKEN_DECIMALS, mint::authority = metadata)]
#[account(init, address = TOKEN_MINT_ADDRESS, payer = signer, mint::decimals = TOKEN_DECIMALS, mint::authority = treasury)]
pub mint: Account<'info, Mint>,
/// The rent sysvar account.
#[account(address = sysvar::rent::ID)]
pub rent: Sysvar<'info, Rent>,
/// The treasury account.
#[account(init, seeds = [TREASURY], bump, payer = signer, space = 8 + size_of::<Treasury>())]
pub treasury: Account<'info, Treasury>,
/// The treasury token account.
#[account(init, associated_token::mint = mint, associated_token::authority = treasury, payer = signer)]
pub treasury_tokens: Account<'info, TokenAccount>,
/// The Solana system program.
#[account(address = system_program::ID)]
@@ -460,6 +414,14 @@ pub struct InitializeMetadata<'info> {
/// The SPL token program.
#[account(address = anchor_spl::token::ID)]
pub token_program: Program<'info, token::Token>,
/// The SPL associated token program.
#[account(address = anchor_spl::associated_token::ID)]
pub associated_token_program: Program<'info, associated_token::AssociatedToken>,
/// The rent sysvar account.
#[account(address = sysvar::rent::ID)]
pub rent: Sysvar<'info, Rent>,
}
#[derive(Accounts)]
@@ -468,9 +430,9 @@ pub struct InitializeBusses<'info> {
#[account(mut)]
pub signer: Signer<'info>,
/// The metadata account.
#[account(seeds = [METADATA], bump = metadata.bump)]
pub metadata: Account<'info, Metadata>,
/// The treasury account.
#[account(seeds = [TREASURY], bump = treasury.bump)]
pub treasury: Account<'info, Treasury>,
/// The Ore token mint account.
#[account(address = TOKEN_MINT_ADDRESS)]
@@ -513,45 +475,6 @@ pub struct InitializeBusses<'info> {
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct InitializeBusTokens<'info> {
/// The signer of the transaction.
#[account(mut)]
pub signer: Signer<'info>,
/// The metadata account.
#[account(seeds = [METADATA], bump = metadata.bump)]
pub metadata: Account<'info, Metadata>,
/// The Ore token mint account.
#[account(address = TOKEN_MINT_ADDRESS)]
pub mint: Account<'info, Mint>,
/// The bus account.
#[account(constraint = bus.id.lt(&BUS_COUNT) @ ProgramError::BusInvalid)]
pub bus: Account<'info, Bus>,
/// The bus token account.
#[account(init, associated_token::mint = mint, associated_token::authority = bus, payer = signer, constraint = bus.id.lt(&BUS_COUNT))]
pub bus_tokens: Account<'info, TokenAccount>,
/// The rent sysvar account.
#[account(address = sysvar::rent::ID)]
pub rent: Sysvar<'info, Rent>,
/// The Solana system program.
#[account(address = system_program::ID)]
pub system_program: Program<'info, System>,
/// The SPL token program.
#[account(address = anchor_spl::token::ID)]
pub token_program: Program<'info, token::Token>,
/// The SPL associated token program.
#[account(address = anchor_spl::associated_token::ID)]
pub associated_token_program: Program<'info, associated_token::AssociatedToken>,
}
/// InitializeProof initializes a new proof account for a miner.
#[derive(Accounts)]
pub struct InitializeProof<'info> {
@@ -607,53 +530,21 @@ pub struct ResetEpoch<'info> {
#[account(mut, seeds = [BUS, &[7]], bump = bus_7.bump)]
pub bus_7: Box<Account<'info, Bus>>,
/// Bus token account 0.
#[account(mut, associated_token::mint = mint, associated_token::authority = bus_0)]
pub bus_0_tokens: Box<Account<'info, TokenAccount>>,
/// Bus token account 1.
#[account(mut, associated_token::mint = mint, associated_token::authority = bus_1)]
pub bus_1_tokens: Box<Account<'info, TokenAccount>>,
/// Bus token account 2.
#[account(mut, associated_token::mint = mint, associated_token::authority = bus_2)]
pub bus_2_tokens: Box<Account<'info, TokenAccount>>,
/// Bus token account 3.
#[account(mut, associated_token::mint = mint, associated_token::authority = bus_3)]
pub bus_3_tokens: Box<Account<'info, TokenAccount>>,
/// Bus token account 4.
#[account(mut, associated_token::mint = mint, associated_token::authority = bus_4)]
pub bus_4_tokens: Box<Account<'info, TokenAccount>>,
/// Bus token account 5.
#[account(mut, associated_token::mint = mint, associated_token::authority = bus_5)]
pub bus_5_tokens: Box<Account<'info, TokenAccount>>,
/// Bus token account 6.
#[account(mut, associated_token::mint = mint, associated_token::authority = bus_6)]
pub bus_6_tokens: Box<Account<'info, TokenAccount>>,
/// Bus token account 7.
#[account(mut, associated_token::mint = mint, associated_token::authority = bus_7)]
pub bus_7_tokens: Box<Account<'info, TokenAccount>>,
/// The Ore token mint account.
#[account(mut, address = TOKEN_MINT_ADDRESS)]
pub mint: Account<'info, Mint>,
/// The metadata account.
#[account(mut, seeds = [METADATA], bump = metadata.bump)]
pub metadata: Account<'info, Metadata>,
/// The treasury account.
#[account(mut, seeds = [TREASURY], bump = treasury.bump)]
pub treasury: Account<'info, Treasury>,
/// The treasury token account.
#[account(mut, associated_token::mint = mint, associated_token::authority = treasury)]
pub treasury_tokens: Account<'info, TokenAccount>,
/// The SPL token program.
#[account(address = anchor_spl::token::ID)]
pub token_program: Program<'info, token::Token>,
/// The SPL associated token program.
#[account(address = anchor_spl::associated_token::ID)]
pub associated_token_program: Program<'info, associated_token::AssociatedToken>,
}
/// Mine distributes Ore to the beneficiary if the signer provides a valid hash.
@@ -664,22 +555,10 @@ pub struct Mine<'info> {
#[account(mut, address = proof.authority)]
pub signer: Signer<'info>,
/// The beneficiary token account to mint rewards to.
#[account(mut, token::mint = mint)]
pub beneficiary: Account<'info, TokenAccount>,
/// A bus account.
#[account(mut, constraint = bus.id.lt(&BUS_COUNT) @ ProgramError::BusInvalid)]
pub bus: Account<'info, Bus>,
/// The bus' token account.
#[account(mut, associated_token::mint = mint, associated_token::authority = bus)]
pub bus_tokens: Account<'info, TokenAccount>,
/// The metadata account.
#[account(seeds = [METADATA], bump = metadata.bump)]
pub metadata: Account<'info, Metadata>,
/// The proof account.
#[account(mut, seeds = [PROOF, signer.key().as_ref()], bump = proof.bump)]
pub proof: Account<'info, Proof>,
@@ -688,6 +567,10 @@ pub struct Mine<'info> {
#[account(address = TOKEN_MINT_ADDRESS)]
pub mint: Account<'info, Mint>,
/// The treasury account.
#[account(seeds = [TREASURY], bump = treasury.bump)]
pub treasury: Account<'info, Treasury>,
/// The SPL token program.
#[account(address = anchor_spl::token::ID)]
pub token_program: Program<'info, token::Token>,
@@ -698,6 +581,38 @@ pub struct Mine<'info> {
pub slot_hashes: AccountInfo<'info>,
}
#[derive(Accounts)]
#[instruction(amount: u64)]
pub struct Claim<'info> {
/// The signer of the transaction (i.e. the miner).
#[account(mut, address = proof.authority)]
pub signer: Signer<'info>,
/// The beneficiary token account to distribute rewards to.
#[account(mut, token::mint = mint)]
pub beneficiary: Account<'info, TokenAccount>,
/// The proof account.
#[account(mut, seeds = [PROOF, signer.key().as_ref()], bump = proof.bump)]
pub proof: Account<'info, Proof>,
/// The Ore token mint account.
#[account(address = TOKEN_MINT_ADDRESS)]
pub mint: Account<'info, Mint>,
/// The treasury account.
#[account(seeds = [TREASURY], bump = treasury.bump)]
pub treasury: Account<'info, Treasury>,
/// The treasury token account.
#[account(mut, associated_token::mint = mint, associated_token::authority = treasury)]
pub treasury_tokens: Account<'info, TokenAccount>,
/// The SPL token program.
#[account(address = anchor_spl::token::ID)]
pub token_program: Program<'info, token::Token>,
}
/// UpdateAdmin allows the admin to reassign the admin authority.
#[derive(Accounts)]
#[instruction(new_admin: Pubkey)]
@@ -706,9 +621,9 @@ pub struct UpdateAdmin<'info> {
#[account(mut)]
pub signer: Signer<'info>,
/// The metadata account.
#[account(seeds = [METADATA], bump = metadata.bump, constraint = metadata.admin.eq(&signer.key()) @ ProgramError::NotAuthorized)]
pub metadata: Account<'info, Metadata>,
/// The treasury account.
#[account(seeds = [TREASURY], bump = treasury.bump, constraint = treasury.admin.eq(&signer.key()) @ ProgramError::NotAuthorized)]
pub treasury: Account<'info, Treasury>,
}
/// UpdateDifficulty allows the admin to update the mining difficulty.
@@ -719,9 +634,9 @@ pub struct UpdateDifficulty<'info> {
#[account(mut)]
pub signer: Signer<'info>,
/// The metadata account.
#[account(seeds = [METADATA], bump = metadata.bump, constraint = metadata.admin.eq(&signer.key()) @ ProgramError::NotAuthorized)]
pub metadata: Account<'info, Metadata>,
/// The treasury account.
#[account(seeds = [TREASURY], bump = treasury.bump, constraint = treasury.admin.eq(&signer.key()) @ ProgramError::NotAuthorized)]
pub treasury: Account<'info, Treasury>,
}
/// MineEvent logs revelant data about a successful Ore mining transaction.
@@ -766,6 +681,10 @@ pub enum ProgramError {
BusInsufficientFunds,
#[msg("The signer is not authorized to perform this action")]
NotAuthorized,
#[msg("You cannot claim more tokens than are available")]
ClaimTooLarge,
#[msg("The treasury does not have enough tokens to honor the claim")]
TreasuryInsufficientFunds,
}
#[cfg(test)]